예제 #1
0
    def __call__(self, iterative):
        r'''When this point is reached, a complete time integration step was
           finished and PLUMED should be notified about this.
        '''
        if not self.hooked:
            if log.do_high:
                log.hline()
                log("Reinitializing PLUMED")
                log.hline()
            if log.do_warning:
                log.warn("You are using PLUMED as a hook for your integrator. "
                         "If PLUMED adds time-dependent forces (for instance "
                         "when performing metadynamics) there is no energy "
                         "conservation. The conserved quantity reported by "
                         "YAFF is irrelevant in this case.")
            self.setup_plumed(timestep=iterative.timestep,
                              restart=iterative.counter > 0)
            self.hooked = True
        # PLUMED provides a setEnergy command, which should pass the
        # current potential energy. It seems that this is never used, so we
        # don't pass anything for the moment.
#        current_energy = sum([part.energy for part in iterative.ff.parts[:-1] if not isinstance(part, ForcePartPlumed)])
#        self.plumed.cmd("setEnergy", current_energy)
        self.plumedstep = iterative.counter
        self._internal_compute(None, None)
        self.plumed.cmd("update")
예제 #2
0
파일: generator.py 프로젝트: tovrstra/yaff
def apply_generators(system, parameters, ff_args):
    '''Populate the attributes of ff_args, prepares arguments for ForceField

       **Arguments:**

       system
            A System instance for which the force field object is being made

       ff_args
            An instance of the FFArgs class.

       parameters
            An instance of the Parameters, typically made by
            ``Parmaeters.from_file('parameters.txt')``.
    '''

    # Collect all the generators that have a prefix.
    generators = {}
    for x in globals().values():
        if isinstance(x, type) and issubclass(x, Generator) and x.prefix is not None:
            generators[x.prefix] = x()

    # Go through all the sections of the parameter file and apply the
    # corresponding generator.
    for prefix, section in parameters.sections.iteritems():
        generator = generators.get(prefix)
        if generator is None:
            if log.do_warning:
                log.warn('There is no generator named %s.' % prefix)
        else:
            generator(system, section, ff_args)
예제 #3
0
파일: generator.py 프로젝트: tovrstra/yaff
    def apply(self, par_table, system, ff_args):
        '''Generate terms for the system based on the par_table

           **Arguments:**

           par_table
                A dictionary with tuples of ffatypes is keys and lists of
                parameters as values.

           system
                The system for which the force field is generated.

           ff_args
                An instance of the FFArgs class.
        '''
        if system.bonds is None:
            raise ValueError('The system must have bonds in order to define valence cross terms.')
        part_valence = ff_args.get_part_valence(system)
        for indexes in self.iter_indexes(system):
            key = tuple(system.get_ffatype(i) for i in indexes)
            par_list = par_table.get(key, [])
            if len(par_list) == 0 is None and log.do_warning:
                log.warn('No valence %s parameters found for atoms %s with key %s' % (self.prefix, indexes, key))
                continue
            for pars in par_list:
                indexes0 = self.get_indexes0(indexes)
                indexes1 = self.get_indexes1(indexes)
                indexes2 = self.get_indexes2(indexes)
                args_01 = (pars[0], pars[3], pars[4]) + (self.ICClass0(*indexes0), self.ICClass1(*indexes1))
                args_02 = (pars[1], pars[3], pars[5]) + (self.ICClass0(*indexes0), self.ICClass2(*indexes2))
                args_12 = (pars[2], pars[4], pars[5]) + (self.ICClass1(*indexes1), self.ICClass2(*indexes2))
                part_valence.add_term(self.VClass01(*args_01))
                part_valence.add_term(self.VClass02(*args_02))
                part_valence.add_term(self.VClass12(*args_12))
예제 #4
0
파일: generator.py 프로젝트: tovrstra/yaff
    def apply(self, par_table, system, ff_args):
        '''Generate terms for the system based on the par_table

           **Arguments:**

           par_table
                A dictionary with tuples of ffatypes is keys and lists of
                parameters as values.

           system
                The system for which the force field is generated.

           ff_args
                An instance of the FFArgs class.
        '''
        if system.bonds is None:
            raise ValueError('The system must have bonds in order to define valence terms.')
        part_valence = ff_args.get_part_valence(system)
        for indexes in self.iter_indexes(system):
            key = tuple(system.get_ffatype(i) for i in indexes)
            par_list = par_table.get(key, [])
            if len(par_list) == 0 and log.do_warning:
                log.warn('No valence %s parameters found for atoms %s with key %s' % (self.prefix, indexes, key))
                continue
            for pars in par_list:
                vterm = self.get_vterm(pars, indexes)
                part_valence.add_term(vterm)
예제 #5
0
파일: generator.py 프로젝트: tovrstra/yaff
    def apply(self, par_table, scale_table, system, ff_args):
        # Prepare the atomic parameters
        sigmas = np.zeros(system.natom)
        epsilons = np.zeros(system.natom)
        onlypaulis = np.zeros(system.natom, np.int32)
        for i in xrange(system.natom):
            key = (system.get_ffatype(i),)
            par_list = par_table.get(key, [])
            if len(par_list) == 0:
                if log.do_warning:
                    log.warn('No MM3 parameters found for atom %i with fftype %s.' % (i, system.get_ffatype(i)))
            else:
                sigmas[i], epsilons[i], onlypaulis[i] = par_list[0]

        # Prepare the global parameters
        scalings = Scalings(system, scale_table[1], scale_table[2], scale_table[3])

        # Get the part. It should not exist yet.
        part_pair = ff_args.get_part_pair(PairPotMM3)
        if part_pair is not None:
            raise RuntimeError('Internal inconsistency: the MM3 part should not be present yet.')

        pair_pot = PairPotMM3(sigmas, epsilons, onlypaulis, ff_args.rcut, ff_args.tr)
        nlist = ff_args.get_nlist(system)
        part_pair = ForcePartPair(system, nlist, scalings, pair_pot)
        ff_args.parts.append(part_pair)
예제 #6
0
def apply_lammps_generators(system, parameters):
    '''Populate the attributes of ff_args, prepares arguments for ForceField

       **Arguments:**

       system
            A System instance for which the force field object is being made

       parameters
            An instance of the Parameters, typically made by
            ``Parmaeters.from_file('parameters.txt')``.
    '''

    # Collect all the generators that have a prefix.
    generators = {}
    for x in globals().values():
        if isinstance(x, type) and issubclass(
                x, Generator) and x.prefix is not None:
            generators[x.prefix] = x()

    output = {}
    # Go through all the sections of the parameter file and apply the
    # corresponding generator.
    for prefix, section in parameters.sections.items():
        generator = generators.get(prefix)
        if generator is None:
            if log.do_warning:
                log.warn(
                    'There is no generator named %s. It will be ignored.' %
                    prefix)
        else:
            output[prefix] = generator(system, section)
    return output
예제 #7
0
파일: opt.py 프로젝트: molmod/yaff
 def propagate(self):
     success = self.minimizer.propagate()
     self.x = self.minimizer.x
     if success == False:
         if log.do_warning:
             log.warn('Line search failed in optimizer. Aborting optimization. This is probably due to a dicontinuity in the energy or the forces. Check the truncation of the non-bonding interactions and the Ewald summation parameters.')
         return True
     return BaseOptimizer.propagate(self)
예제 #8
0
파일: system.py 프로젝트: tovrstra/yaff
 def set_standard_masses(self):
     """Initialize the ``masses`` attribute based on the atomic numbers."""
     with log.section('SYS'):
         from molmod.periodic import periodic
         if self.masses is not None:
             if log.do_warning:
                 log.warn('Overwriting existing masses with default masses.')
         self.masses = np.array([periodic[n].mass for n in self.numbers])
예제 #9
0
파일: opt.py 프로젝트: boegel/yaff
 def propagate(self):
     success = self.minimizer.propagate()
     self.x = self.minimizer.x
     if success == False:
         if log.do_warning:
             log.warn('Line search failed in optimizer. Aborting optimization. This is probably due to a dicontinuity in the energy or the forces. Check the truncation of the non-bonding interactions and the Ewald summation parameters.')
         return True
     return BaseOptimizer.propagate(self)
예제 #10
0
파일: system.py 프로젝트: mcoolsce/yaff
 def set_standard_masses(self):
     """Initialize the ``masses`` attribute based on the atomic numbers."""
     with log.section('SYS'):
         from molmod.periodic import periodic
         if self.masses is not None:
             if log.do_warning:
                 log.warn('Overwriting existing masses with default masses.')
         self.masses = np.array([periodic[n].mass for n in self.numbers])
예제 #11
0
파일: gaussian.py 프로젝트: molmod/yaff
def g09log_to_hdf5(f, fn_log):
    """Convert Gaussian09 BOMD log file to Yaff HDF5 format.

       **Arguments:**

       f
            An open and writable HDF5 file.

       fn_log
            The name of the Gaussian log file.
    """
    with log.section('G09H5'):
        if log.do_medium:
            log('Loading Gaussian 09 file \'%s\' into \'trajectory\' of HDF5 file \'%s\'' % (
                fn_log, f.filename
            ))

        # First make sure the HDF5 file has a system description that is consistent
        # with the XYZ file.
        if 'system' not in f:
            raise ValueError('The HDF5 file must contain a system group.')
        if 'numbers' not in f['system']:
            raise ValueError('The HDF5 file must have a system group with atomic numbers.')
        natom = f['system/numbers'].shape[0]

        # Take care of the trajectory group
        tgrp = get_trajectory_group(f)

        # Take care of the pos and vel datasets
        dss = get_trajectory_datasets(tgrp,
            ('pos', (natom, 3)),
            ('vel', (natom, 3)),
            ('frc', (natom, 3)),
            ('time', (1,)),
            ('step', (1,)),
            ('epot', (1,)),
            ('ekin', (1,)),
            ('etot', (1,)),
        )
        ds_pos, ds_vel, ds_frc, ds_time, ds_step, ds_epot, ds_ekin, ds_etot = dss

        # Load frame by frame
        row = get_last_trajectory_row(dss)
        for numbers, pos, vel, frc, time, step, epot, ekin, etot in _iter_frames_g09(fn_log):
            if (numbers != f['system/numbers']).any():
                log.warn('The element numbers of the HDF5 and LOG file do not match.')
            write_to_dataset(ds_pos, pos, row)
            write_to_dataset(ds_vel, vel, row)
            write_to_dataset(ds_frc, frc, row)
            write_to_dataset(ds_time, time, row)
            write_to_dataset(ds_step, step, row)
            write_to_dataset(ds_epot, epot, row)
            write_to_dataset(ds_ekin, ekin, row)
            write_to_dataset(ds_etot, etot, row)
            row += 1

        # Check number of rows
        check_trajectory_rows(tgrp, dss, row)
예제 #12
0
파일: generator.py 프로젝트: tovrstra/yaff
    def apply(self, par_table, cpar_table, scale_table, mixing_rules, system, ff_args):
        # Prepare the atomic parameters
        amps = np.zeros(system.nffatype, float)
        bs = np.zeros(system.nffatype, float)
        for i in xrange(system.nffatype):
            key = (system.ffatypes[i],)
            par_list = par_table.get(key, [])
            if len(par_list) == 0:
                if log.do_warning:
                    log.warn('No EXPREP parameters found for ffatype %s.' % system.ffatypes[i])
            else:
                amps[i], bs[i] = par_list[0]

        # Prepare the cross parameters
        amp_cross = np.zeros((system.nffatype, system.nffatype), float)
        b_cross = np.zeros((system.nffatype, system.nffatype), float)
        for i0 in xrange(system.nffatype):
            for i1 in xrange(i0+1):
                cpar_list = cpar_table.get((system.ffatypes[i0], system.ffatypes[i1]), [])
                if len(cpar_list) == 0:
                    if log.do_high:
                        log('No EXPREP cross parameters found for ffatypes %s,%s. Mixing rule will be used' % (system.ffatypes[i0], system.ffatypes[i1]))
                else:
                    amp_cross[i0,i1], b_cross[i0,i1] = cpar_list[0]
                    if i0 != i1:
                        amp_cross[i1,i0], b_cross[i1,i0] = cpar_list[0]

        # Prepare the global parameters
        scalings = Scalings(system, scale_table[1], scale_table[2], scale_table[3])
        amp_mix, amp_mix_coeff = mixing_rules['A']
        if amp_mix == 0:
            amp_mix_coeff = 0.0
        elif amp_mix == 1:
            amp_mix_coeff = amp_mix_coeff[0]
        b_mix, b_mix_coeff = mixing_rules['B']
        if b_mix == 0:
            b_mix_coeff = 0.0
        elif b_mix == 1:
            b_mix_coeff = b_mix_coeff[0]

        # Get the part. It should not exist yet.
        part_pair = ff_args.get_part_pair(PairPotExpRep)
        if part_pair is not None:
            raise RuntimeError('Internal inconsistency: the EXPREP part should not be present yet.')

        pair_pot = PairPotExpRep(
            system.ffatype_ids, amp_cross, b_cross, ff_args.rcut, ff_args.tr,
            amps, amp_mix, amp_mix_coeff, bs, b_mix, b_mix_coeff,
        )
        nlist = ff_args.get_nlist(system)
        part_pair = ForcePartPair(system, nlist, scalings, pair_pot)
        ff_args.parts.append(part_pair)
예제 #13
0
    def _verify_hooks(self):
        with log.section('ENSEM'):
            thermo = None
            index_thermo = 0
            baro = None
            index_baro = 0

            # Look for the presence of a thermostat and/or barostat
            if hasattr(self.hooks, '__len__'):
                for index, hook in enumerate(self.hooks):
                    if hook.method == 'thermostat':
                        thermo = hook
                        index_thermo = index
                    elif hook.method == 'barostat':
                        baro = hook
                        index_baro = index
            elif self.hooks is not None:
                if self.hooks.method == 'thermostat':
                    thermo = self.hooks
                elif self.hooks.method == 'barostat':
                    baro = self.hooks

            # If both are present, delete them and generate TBCombination element
            if thermo is not None and baro is not None:
                from yaff.sampling.npt import TBCombination
                if log.do_warning:
                    log.warn(
                        'Both thermostat and barostat are present separately and will be merged'
                    )
                del self.hooks[max(index_thermo, index_thermo)]
                del self.hooks[min(index_thermo, index_baro)]
                self.hooks.append(TBCombination(thermo, baro))

            if hasattr(self.hooks, '__len__'):
                for hook in self.hooks:
                    if hook.name == 'TBCombination':
                        thermo = hook.thermostat
                        baro = hook.barostat
            elif self.hooks is not None:
                if self.hooks.name == 'TBCombination':
                    thermo = self.hooks.thermostat
                    baro = self.hooks.barostat

            if log.do_warning:
                if thermo is not None:
                    log('Temperature coupling achieved through ' +
                        str(thermo.name) + ' thermostat')
                if baro is not None:
                    log('Pressure coupling achieved through ' +
                        str(baro.name) + ' barostat')
예제 #14
0
파일: verlet.py 프로젝트: molmod/yaff
    def _verify_hooks(self):
        with log.section('ENSEM'):
            thermo = None
            index_thermo = 0
            baro = None
            index_baro = 0

            # Look for the presence of a thermostat and/or barostat
            if hasattr(self.hooks, '__len__'):
                for index, hook in enumerate(self.hooks):
                    if hook.method == 'thermostat':
                        thermo = hook
                        index_thermo = index
                    elif hook.method == 'barostat':
                        baro = hook
                        index_baro = index
            elif self.hooks is not None:
                if self.hooks.method == 'thermostat':
                    thermo = self.hooks
                elif self.hooks.method == 'barostat':
                    baro = self.hooks

            # If both are present, delete them and generate TBCombination element
            if thermo is not None and baro is not None:
                from yaff.sampling.npt import TBCombination
                if log.do_warning:
                    log.warn('Both thermostat and barostat are present separately and will be merged')
                del self.hooks[max(index_thermo, index_thermo)]
                del self.hooks[min(index_thermo, index_baro)]
                self.hooks.append(TBCombination(thermo, baro))

            if hasattr(self.hooks, '__len__'):
                for hook in self.hooks:
                    if hook.name == 'TBCombination':
                        thermo = hook.thermostat
                        baro = hook.barostat
            elif self.hooks is not None:
                if self.hooks.name == 'TBCombination':
                    thermo = self.hooks.thermostat
                    baro = self.hooks.barostat

            if log.do_warning:
                if thermo is not None:
                    log('Temperature coupling achieved through ' + str(thermo.name) + ' thermostat')
                if baro is not None:
                    log('Pressure coupling achieved through ' + str(baro.name) + ' barostat')
예제 #15
0
    def detect_ffatypes(self, rules):
        """Initialize the ``ffatypes`` attribute based on ATSELECT rules.

           **Argument:**

           rules
                A list of (ffatype, rule) pairs that will be used to initialize
                the attributes ``self.ffatypes`` and ``self.ffatype_ids``.

           If the system already has FF atom types, they will be overwritten.
        """
        with log.section('SYS'):
            # Give warning if needed
            if self.ffatypes is not None:
                if log.do_warning:
                    log.warn('Overwriting existing FF atom types.')
            # Compile all the rules
            my_rules = []
            for ffatype, rule in rules:
                check_name(ffatype)
                if isinstance(rule, str):
                    rule = atsel_compile(rule)
                my_rules.append((ffatype, rule))
            # Use the rules to detect the atom types
            lookup = {}
            self.ffatypes = []
            self.ffatype_ids = np.zeros(self.natom, int)
            for i in range(self.natom):
                my_ffatype = None
                for ffatype, rule in my_rules:
                    if rule(self, i):
                        my_ffatype = ffatype
                        break
                if my_ffatype is None:
                    raise ValueError(
                        'Could not detect FF atom type of atom %i.' % i)
                ffatype_id = lookup.get(my_ffatype)
                if ffatype_id is None:
                    ffatype_id = len(lookup)
                    self.ffatypes.append(my_ffatype)
                    lookup[my_ffatype] = ffatype_id
                self.ffatype_ids[i] = ffatype_id
            # Make sure all is done well ...
            self._init_derived_ffatypes()
예제 #16
0
파일: system.py 프로젝트: tovrstra/yaff
    def detect_ffatypes(self, rules):
        """Initialize the ``ffatypes`` attribute based on ATSELECT rules.

           **Argument:**

           rules
                A list of (ffatype, rule) pairs that will be used to initialize
                the attributes ``self.ffatypes`` and ``self.ffatype_ids``.

           If the system already has FF atom types, they will be overwritten.
        """
        with log.section('SYS'):
            # Give warning if needed
            if self.ffatypes is not None:
                if log.do_warning:
                    log.warn('Overwriting existing FF atom types.')
            # Compile all the rules
            my_rules = []
            for ffatype, rule in rules:
                check_name(ffatype)
                if isinstance(rule, basestring):
                    rule = atsel_compile(rule)
                my_rules.append((ffatype, rule))
            # Use the rules to detect the atom types
            lookup = {}
            self.ffatypes = []
            self.ffatype_ids = np.zeros(self.natom, int)
            for i in xrange(self.natom):
                my_ffatype = None
                for ffatype, rule in my_rules:
                    if rule(self, i):
                        my_ffatype = ffatype
                        break
                if my_ffatype is None:
                    raise ValueError('Could not detect FF atom type of atom %i.' % i)
                ffatype_id = lookup.get(my_ffatype)
                if ffatype_id is None:
                    ffatype_id = len(lookup)
                    self.ffatypes.append(my_ffatype)
                    lookup[my_ffatype] = ffatype_id
                self.ffatype_ids[i] = ffatype_id
            # Make sure all is done well ...
            self._init_derived_ffatypes()
예제 #17
0
파일: generator.py 프로젝트: tovrstra/yaff
    def apply(self, atom_table, bond_table, scale_table, dielectric, system, ff_args):
        if system.charges is None:
            system.charges = np.zeros(system.natom)
        elif log.do_warning and abs(system.charges).max() != 0:
            log.warn('Overwriting charges in system.')
        system.charges[:] = 0.0
        system.radii = np.zeros(system.natom)

        # compute the charges
        for i in xrange(system.natom):
            pars = atom_table.get(system.get_ffatype(i))
            if pars is not None:
                charge, radius = pars
                system.charges[i] += charge
                system.radii[i] = radius
            elif log.do_warning:
                log.warn('No charge defined for atom %i with fftype %s.' % (i, system.get_ffatype(i)))
        for i0, i1 in system.iter_bonds():
            ffatype0 = system.get_ffatype(i0)
            ffatype1 = system.get_ffatype(i1)
            if ffatype0 == ffatype1:
                continue
            charge_transfer = bond_table.get((ffatype0, ffatype1))
            if charge_transfer is None:
                if log.do_warning:
                    log.warn('No charge transfer parameter for atom pair (%i,%i) with fftype (%s,%s).' % (i0, i1, system.get_ffatype(i0), system.get_ffatype(i1)))
            else:
                system.charges[i0] += charge_transfer
                system.charges[i1] -= charge_transfer

        # prepare other parameters
        scalings = Scalings(system, scale_table[1], scale_table[2], scale_table[3])

        # Setup the electrostatic pars
        ff_args.add_electrostatic_parts(system, scalings, dielectric)
예제 #18
0
파일: generator.py 프로젝트: tovrstra/yaff
    def apply(self, par_table, cpar_table, scale_table, system, ff_args):
        # Prepare the atomic parameters
        c6s = np.zeros(system.nffatype, float)
        bs = np.zeros(system.nffatype, float)
        vols = np.zeros(system.nffatype, float)
        for i in xrange(system.nffatype):
            key = (system.ffatypes[i],)
            par_list = par_table.get(key, [])
            if len(par_list) == 0:
                if log.do_warning:
                    log.warn('No DAMPDISP parameters found for atom %i with fftype %s.' % (i, system.get_ffatype(i)))
            else:
                c6s[i], bs[i], vols[i] = par_list[0]

        # Prepare the cross parameters
        c6_cross = np.zeros((system.nffatype, system.nffatype), float)
        b_cross = np.zeros((system.nffatype, system.nffatype), float)
        for i0 in xrange(system.nffatype):
            for i1 in xrange(i0+1):
                cpar_list = cpar_table.get((system.ffatypes[i0], system.ffatypes[i1]), [])
                if len(cpar_list) == 0:
                    if log.do_high:
                        log('No DAMPDISP cross parameters found for ffatypes %s,%s. Mixing rule will be used' % (system.ffatypes[i0], system.ffatypes[i1]))
                else:
                    c6_cross[i0,i1], b_cross[i0,i1] = cpar_list[0]
                    if i0 != i1:
                        c6_cross[i1,i0], b_cross[i1,i0] = cpar_list[0]

        # Prepare the global parameters
        scalings = Scalings(system, scale_table[1], scale_table[2], scale_table[3])

        # Get the part. It should not exist yet.
        part_pair = ff_args.get_part_pair(PairPotDampDisp)
        if part_pair is not None:
            raise RuntimeError('Internal inconsistency: the DAMPDISP part should not be present yet.')

        pair_pot = PairPotDampDisp(system.ffatype_ids, c6_cross, b_cross, ff_args.rcut, ff_args.tr, c6s, bs, vols)
        nlist = ff_args.get_nlist(system)
        part_pair = ForcePartPair(system, nlist, scalings, pair_pot)
        ff_args.parts.append(part_pair)
예제 #19
0
    def apply(self, atom_table, bond_table, scale_table, dielectric, system):
        if system.charges is None:
            system.charges = np.zeros(system.natom)
        elif log.do_warning and abs(system.charges).max() != 0:
            log.warn('Overwriting charges in system.')
        system.charges[:] = 0.0
        system.radii = np.zeros(system.natom)

        # compute the charges
        for i in range(system.natom):
            pars = atom_table.get(system.get_ffatype(i))
            if pars is not None:
                charge, radius = pars
                system.charges[i] += charge
                system.radii[i] = radius
            elif log.do_warning:
                log.warn('No charge defined for atom %i with fftype %s.' %
                         (i, system.get_ffatype(i)))
        for i0, i1 in system.iter_bonds():
            ffatype0 = system.get_ffatype(i0)
            ffatype1 = system.get_ffatype(i1)
            if ffatype0 == ffatype1:
                continue
            charge_transfer = bond_table.get((ffatype0, ffatype1))
            if charge_transfer is None:
                if log.do_warning:
                    log.warn(
                        'No charge transfer parameter for atom pair (%i,%i) with fftype (%s,%s).'
                        % (i0, i1, system.get_ffatype(i0),
                           system.get_ffatype(i1)))
            else:
                system.charges[i0] += charge_transfer
                system.charges[i1] -= charge_transfer
예제 #20
0
파일: system.py 프로젝트: tovrstra/yaff
    def detect_bonds(self, exceptions=None):
        """Initialize the ``bonds`` attribute based on inter-atomic distances

           **Optional argument:**

           exceptions:
                Specify custom threshold for certain pairs of elements. This
                must be a dictionary with ((num0, num1), threshold) as items.

           For each pair of elements, a distance threshold is used to detect
           bonded atoms. The distance threshold is based on a database of known
           bond lengths. If the database does not contain a record for the given
           element pair, the threshold is based on the sum of covalent radii.
        """
        with log.section('SYS'):
            from molmod.bonds import bonds
            if self.bonds is not None:
                if log.do_warning:
                    log.warn('Overwriting existing bonds.')
            work = np.zeros((self.natom*(self.natom-1))/2, float)
            self.cell.compute_distances(work, self.pos)
            ishort = (work < bonds.max_length*1.01).nonzero()[0]
            new_bonds = []
            for i in ishort:
                i0, i1 = _unravel_triangular(i)
                n0 = self.numbers[i0]
                n1 = self.numbers[i1]
                if exceptions is not None:
                    threshold = exceptions.get((n0, n1))
                    if threshold is None and n0!=n1:
                        threshold = exceptions.get((n1, n0))
                    if threshold is not None:
                        if work[i] < threshold:
                            new_bonds.append([i0, i1])
                        continue
                if bonds.bonded(n0, n1, work[i]):
                    new_bonds.append([i0, i1])
            self.bonds = np.array(new_bonds)
            self._init_derived_bonds()
예제 #21
0
    def detect_bonds(self, exceptions=None):
        """Initialize the ``bonds`` attribute based on inter-atomic distances

           **Optional argument:**

           exceptions:
                Specify custom threshold for certain pairs of elements. This
                must be a dictionary with ((num0, num1), threshold) as items.

           For each pair of elements, a distance threshold is used to detect
           bonded atoms. The distance threshold is based on a database of known
           bond lengths. If the database does not contain a record for the given
           element pair, the threshold is based on the sum of covalent radii.
        """
        with log.section('SYS'):
            from molmod.bonds import bonds
            if self.bonds is not None:
                if log.do_warning:
                    log.warn('Overwriting existing bonds.')
            work = np.zeros((self.natom * (self.natom - 1)) // 2, float)
            self.cell.compute_distances(work, self.pos)
            ishort = (work < bonds.max_length * 1.01).nonzero()[0]
            new_bonds = []
            for i in ishort:
                i0, i1 = _unravel_triangular(i)
                n0 = self.numbers[i0]
                n1 = self.numbers[i1]
                if exceptions is not None:
                    threshold = exceptions.get((n0, n1))
                    if threshold is None and n0 != n1:
                        threshold = exceptions.get((n1, n0))
                    if threshold is not None:
                        if work[i] < threshold:
                            new_bonds.append([i0, i1])
                        continue
                if bonds.bonded(n0, n1, work[i]):
                    new_bonds.append([i0, i1])
            self.bonds = np.array(new_bonds)
            self._init_derived_bonds()
예제 #22
0
def write_pseudo_atoms(ff, workdir):
    """
        Write pseudo_atoms.def file
    """
    system = ff.system
    if system.masses is None: system.set_standard_masses()
    # If no charges are present, we write q=0
    if system.charges is None:
        charges = np.zeros(system.natom)
    else:
        charges = system.charges
    # RASPA cannot handle Gaussian charges
    if system.radii is not None and np.any(system.radii != 0.0):
        if log.do_warning:
            log.warn("Atomic radii were specified, but RASPA will not take "
                     "this into account for electrostatic interactions")
        raise ValueError("Gaussian electrostatics not supported by RASPA")
    # We need to write an entry for each atomtype, specifying the charge. If
    # atoms of the same atomtype show different charges, this means the
    # atomtypes need to be fine grained
    ffatypes, ffatype_ids = get_lammps_ffatypes(ff)
    # Write the file
    with open(os.path.join(workdir, 'pseudo_atoms.def'), 'w') as f:
        f.write("#number of pseudo atoms\n%d\n" % (len(ffatypes)))
        f.write("#type      print     as   chem  oxidation         mass       "
                "charge   polarization B-factor  radii  connectivity "
                "anisotropic anisotropic-type   tinker-type\n")
        for iffa, ffa in enumerate(ffatypes):
            iatom = np.where(ffatype_ids == iffa)[0][0]
            symbol = periodic[system.numbers[iatom]].symbol
            f.write(
                "%-12s yes %6s %6s %10d %12.8f %+32.20f         %6.3f   "
                "%6.3f %6.3f         %5d       %5d       %10s         %5d\n" %
                (ffa, symbol, symbol, 0, system.masses[iatom] / amu,
                 charges[iatom], 0.0, 1.0, 1.0, 0, 0, "absolute", 0))
    return ffatypes, ffatype_ids
예제 #23
0
파일: liblammps.py 프로젝트: jelle-w/yaff
    def __init__(self,
                 ff,
                 fn_system,
                 fn_log="none",
                 suffix='',
                 do_table=True,
                 fn_table='lammps.table',
                 scalings_table=[0.0, 0.0, 1.0],
                 do_ei=True,
                 kspace='ewald',
                 kspace_accuracy=1e-7,
                 scalings_ei=[0.0, 0.0, 1.0],
                 triclinic=True,
                 comm=None,
                 move_central_cell=False):
        r'''Initalize LAMMPS ForcePart

           **Arguments:**

           system
                An instance of the ``System`` class.

           fn_system
                The file containing the system data in LAMMPS format, can be
                generated using external.lammpsio.write_lammps_system_data

           **Optional Arguments:**

           fn_log
                Filename where LAMMPS output is stored. This is probably only
                necessary for debugging. Default: None, which means no output
                is stored

           suffix
                The suffix of the liblammps_*.so library file

           do_table
                Boolean, compute a potentual using tabulated values

           fn_table
                Filename of file containing tabulated non-bonded potential
                without point-charge electrostatics.
                Can be written using the ```write_lammps_table``` method.
                Default: lammps.table

           scalings_vdw
                List containing vdW scaling factors for 1-2, 1-3 and 1-4
                neighbors

           do_ei
                Boolean, compute a point-charge electrostatic contribution

           kspace
                Method to treat long-range electrostatics, should be one of
                ewald or pppm

           kspace_accuracy
                Desired relative error in electrostatic forces
                Default: 1e-6

           scalings_ei
                List containing electrostatic scaling factors for 1-2, 1-3 and
                1-4 neighbors

           triclinic
                Boolean, specify whether a triclinic cell will be used during
                the simulation. If the cell is orthogonal, set it to False
                as LAMMPS should run slightly faster.
                Default: True

           comm
                MPI communicator, required if LAMMPS should run in parallel

           move_central_cell
                Boolean, if True, every atom is moved to the central cell
                (centered at the origin) before passing positions to LAMMPS.
                Change this if LAMMPS gives a Atoms Lost ERROR.
        '''
        self.system = ff.system
        # Try to load the lammps package, quit if not possible
        try:
            from lammps import lammps
        except:
            log("Could not import the lammps python package which is required to use LAMMPS as a library"
                )
            raise ImportError
        # Some safety checks...
        if self.system.cell.nvec != 3:
            raise ValueError(
                'The system must be 3D periodic for LAMMPS calculations.')
        if not os.path.isfile(fn_system):
            raise ValueError('Could not read file %s' % fn_system)
        if do_table:
            if not os.path.isfile(fn_table):
                raise ValueError('Could not read file %s' % fn_table)
            tables = read_lammps_table(fn_table)
            table_names = [table[0] for table in tables]
            npoints, _, rcut = tables[0][1]
        elif do_ei:
            rcut = 0
            for part in ff.parts:
                if part.name == 'pair_ei':
                    rcut = part.pair_pot.rcut
            if rcut == 0:
                log("ERROR, do_ei set to True, but pair_ei was not found in the ff"
                    )
        else:
            raise NotImplementedError
        if not kspace in ['ewald', 'pppm']:
            raise ValueError('kspace should be one of ewald or pppm')
        if self.system.natom > 2000 and kspace == 'ewald' and log.do_warning:
            log.warn("You are simulating a more or less large system."
                     "It might be more efficient to use kspace=pppm")
        if self.system.natom < 1000 and kspace == 'pppm' and log.do_warning:
            log.warn("You are simulating a more or less small system."
                     "It might be better to use kspace=ewald")

        # Initialize a class instance and some attributes
        ForcePart.__init__(self, 'lammps', self.system)
        self.comm = comm
        self.triclinic = triclinic
        self.move_central_cell = move_central_cell
        ffatypes, ffatype_ids = get_lammps_ffatypes(ff)
        nffa = len(ffatypes)

        # Pass all commands that would normally appear in the LAMMPS input file
        # to our instance of LAMMPS.
        self.lammps = lammps(name=suffix,
                             comm=self.comm,
                             cmdargs=["-screen", fn_log, "-log", "none"])
        self.lammps.command("units electron")
        self.lammps.command("atom_style full")
        self.lammps.command("atom_modify map array")
        self.lammps.command("box tilt large")
        self.lammps.command("read_data %s" % fn_system)
        self.lammps.command("mass * 1.0")
        self.lammps.command("bond_style none")

        # Hybrid style combining electrostatics and table
        if do_ei and do_table:
            self.lammps.command(
                "pair_style hybrid/overlay coul/long %13.8f table spline %d" %
                (rcut, npoints))
            self.lammps.command("pair_coeff * * coul/long")
            self.lammps.command("kspace_style %s %10.5e" %
                                (kspace, kspace_accuracy))
        # Only electrostatics
        elif do_ei:
            self.lammps.command("pair_style coul/long %13.8f" % rcut)
            self.lammps.command("pair_coeff * *")
            self.lammps.command("kspace_style %s %10.5e" %
                                (kspace, kspace_accuracy))
        # Only table
        elif do_table:
            self.lammps.command("pair_style table spline %d" % (npoints))
        else:
            raise NotImplementedError
        for i in range(nffa):
            ffai = ffatypes[i]
            for j in range(i, nffa):
                ffaj = ffatypes[j]
                if ffai > ffaj:
                    name = '%s---%s' % (str(ffai), str(ffaj))
                else:
                    name = '%s---%s' % (str(ffaj), str(ffai))
                if do_ei and do_table:
                    self.lammps.command("pair_coeff %d %d table %s %s" %
                                        (i + 1, j + 1, fn_table, name))
                elif do_table:
                    self.lammps.command("pair_coeff %d %d %s %s" %
                                        (i + 1, j + 1, fn_table, name))
        if do_ei is not None:
            self.lammps.command(
                "special_bonds lj %f %f %f coul %f %f %f" %
                (scalings_table[0], scalings_table[1], scalings_table[2],
                 scalings_ei[0], scalings_ei[1], scalings_ei[2]))
        else:
            self.lammps.command(
                "special_bonds lj %f %f %f" %
                (scalings_table[0], scalings_table[1], scalings_table[2]))
        self.lammps.command("neighbor 0.0 bin")
        self.lammps.command("neigh_modify delay 0 every 1 check no")
        self.lammps.command("compute virial all pressure NULL virial")
        self.lammps.command("variable eng equal pe")
        self.lammps.command("variable Wxx equal c_virial[1]")
        self.lammps.command("variable Wyy equal c_virial[2]")
        self.lammps.command("variable Wzz equal c_virial[3]")
        self.lammps.command("variable Wxy equal c_virial[4]")
        self.lammps.command("variable Wxz equal c_virial[5]")
        self.lammps.command("variable Wyz equal c_virial[6]")
        thermo_style = "step time etotal evdwl ecoul elong etail "
        thermo_style += " ".join(["c_virial[%d]" % i for i in range(1, 7)])
        self.lammps.command("thermo_style custom %s" % thermo_style)
        self.lammps.command("fix 1 all nve")
        # LAMMPS needs cell vectors (ax,0,0), (bx,by,0) and (cx,cy,cz)
        # This means we need to perform a rotation to switch between Yaff and
        # LAMMPS coordinates. All information about this rotation is stored
        # in the variables defined below
        self.rvecs = np.eye(3)
        self.cell = Cell(self.rvecs)
        self.rot = np.zeros((3, 3))
        self.vtens_lammps = np.zeros((6, ))
예제 #24
0
    def __init__(self,
                 system,
                 timestep=0.0,
                 restart=0,
                 fn='plumed.dat',
                 kernel=None,
                 fn_log='plumed.log'):
        r'''Initialize a PLUMED ForcePart. More information on the interface
            between PLUMED and MD codes can be found on
            http://tcb.ucas.ac.cn/plumed2/developer-doc/html/_how_to_plumed_your_m_d.html

            Unfortunately, PLUMED partially breaks the orthogonality of the pes
            and the sampling modules in Yaff: PLUMED sometimes requires
            information from the integrator. For example, in metadynamics
            PLUMED needs to know when a time integration step has been
            completed (which is not necessarily after each force calculation).
            ForcePartPlumed therefore also inherits from Hook and
            by attaching this hook to the integrator, it is possible to obtain
            the necessary information from the integrator. Problems within
            PLUMED for this approach to work, particularly in the VES module,
            have been resolved; see the discussion at
            https://groups.google.com/forum/?fromgroups=#!topic/plumed-users/kPZu_tNZtgk

            **Arguments:**

            system
                An instance of the System class

            **Optional Arguments:**

            timestep
                The timestep (in au) of the integrator

            restart
                Set to a value different from 0 to let PLUMED know that this
                is a restarted run

            fn
                A filename from which the PLUMED instructions are read, default
                is plumed.dat

            kernel
                Path to the PLUMED library (something like
                /path/to/libplumedKernel.so). If None is provided, the
                environment variable $PLUMED_KERNEL should point to the library
                file.

            fn_log
                Path to the file where PLUMED logs output, default is
                plumed.log
        '''
        self.system = system
        self.fn = fn
        self.kernel = kernel
        self.fn_log = fn_log
        self.plumedstep = 0
        # TODO In the LAMMPS-PLUMED interface (src/USER-PLUMED/fix_plumed.cpp)
        # it is mentioned that biasing is not possible when tailcorrections are
        # included. Maybe this should be checked...
        # Check cell dimensions, only 0D and 3D systems supported
        if not self.system.cell.nvec in [0, 3]:
            raise NotImplementedError
        # Setup PLUMED by sending commands to the PLUMED API
        self.setup_plumed(timestep, restart)
        # PLUMED requires masses to be set...
        if self.system.masses is None:
            self.system.set_standard_masses()
        # Initialize the ForcePart
        ForcePart.__init__(self, 'plumed', self.system)
        # Initialize the Hook, can't see a reason why start and step could
        # differ from default values
        Hook.__init__(self, start=0, step=1)
        self.hooked = False
        if log.do_warning:
            log.warn("When using PLUMED as a hook for your integrator "
                     "and PLUMED adds time-dependent forces (for instance "
                     "when performing metadynamics), there is no energy "
                     "conservation. The conserved quantity reported by "
                     "YAFF is irrelevant in this case.")
예제 #25
0
파일: system.py 프로젝트: tovrstra/yaff
 def _init_derived_ffatypes(self):
     if self.ffatype_ids is None:
         if len(self.ffatypes) != self.natom:
             raise TypeError('When the ffatype_ids are derived automatically, the length of the ffatypes list must match the number of atoms.')
         lookup = {}
         ffatypes = []
         self.ffatype_ids = np.zeros(self.natom, int)
         for i in xrange(self.natom):
             if self.scope_ids is None:
                 ffatype = self.ffatypes[i]
                 key = ffatype, None
             else:
                 scope_id = self.scope_ids[i]
                 ffatype = self.ffatypes[i]
                 key = ffatype, scope_id
             ffatype_id = lookup.get(key)
             if ffatype_id is None:
                 ffatype_id = len(ffatypes)
                 ffatypes.append(ffatype)
                 lookup[key] = ffatype_id
             self.ffatype_ids[i] = ffatype_id
         self.ffatypes = ffatypes
     for ffatype in self.ffatypes:
         check_name(ffatype)
     # check the range of the ids
     if self.ffatype_ids.min() != 0 or self.ffatype_ids.max() != len(self.ffatypes)-1:
         raise ValueError('The ffatype_ids have incorrect bounds.')
     # differentiate ffatype_ids if the same ffatype_id is used in different
     # scopes
     if self.scopes is not None:
         self.ffatype_id_to_scope_id = {}
         fixed_fids = {}
         for i in xrange(self.natom):
             fid = self.ffatype_ids[i]
             sid = self.ffatype_id_to_scope_id.get(fid)
             if sid is None:
                 self.ffatype_id_to_scope_id[fid] = self.scope_ids[i]
             elif sid != self.scope_ids[i]:
                 # We found the same ffatype_id in a different scope_id. This
                 # must be fixed. First check if we have already a new
                 # scope_id ready
                 sid = self.scope_ids[i]
                 new_fid = fixed_fids.get((sid, fid))
                 if new_fid is None:
                     # No previous new fid create, do it now.
                     new_fid = len(self.ffatypes)
                     # Copy the ffatype label
                     self.ffatypes.append(self.ffatypes[fid])
                     # Keep track of the new fid
                     fixed_fids[(sid, fid)] = new_fid
                     if log.do_warning:
                         log.warn('Atoms with type ID %i in scope %s were changed to type ID %i.' % (fid, self.scopes[sid], new_fid))
                 # Apply the new fid
                 self.ffatype_ids[i] = new_fid
                 self.ffatype_id_to_scope_id[new_fid] = sid
     # Turn the ffatypes in the scopes into array
     if self.ffatypes is not None:
         self.ffatypes = np.array(self.ffatypes, copy=False)
     if self.scopes is not None:
         self.scopes = np.array(self.scopes, copy=False)
     # check the range of the ids
     if self.ffatype_ids.min() != 0 or self.ffatype_ids.max() != len(self.ffatypes)-1:
         raise ValueError('The ffatype_ids have incorrect bounds.')
     if log.do_medium:
         log('The following atom types are present in the system:')
         log.hline()
         if self.scopes is None:
             log('             Atom type   ID   Number of atoms')
             log.hline()
             for ffatype_id, ffatype in enumerate(self.ffatypes):
                 log('%22s  %3i       %3i' % (ffatype, ffatype_id, (self.ffatype_ids==ffatype_id).sum()))
         else:
             log('                 Scope              Atom type   ID   Number of atoms')
             log.hline()
             for ffatype_id, ffatype in enumerate(self.ffatypes):
                 scope = self.scopes[self.ffatype_id_to_scope_id[ffatype_id]]
                 log('%22s %22s  %3i       %3i' % (scope, ffatype, ffatype_id, (self.ffatype_ids==ffatype_id).sum()))
         log.hline()
         log.blank()
예제 #26
0
    def run(self, nsteps, mc_moves=None, initial=None, einit=0,
                translation_stepsize=1.0*angstrom,
                volumechange_stepsize=10.0*angstrom**3,
                close_contact=0.4*angstrom):
        """
           Perform Monte-Carlo steps

           **Arguments:**

           nsteps
                Number of Monte-Carlo steps

           **Optional Arguments:**

           mc_moves
                Dictionary containing relative probabilities of the different
                Monte-Carlo moves. It is not required that the probabilities
                sum to 1, they are normalized automatically. Example

           initial
                System instance describing the initial configuration of guest
                molecules

           einit
                The energy of the initial state

           translation_stepsize
                The maximal magnitude of a TrialTranslation

           volumechange_stepsize
                The maximal magnitude of a TrialVolumechange

           close_contact
                Automatically reject TrialMove if atoms are placed shorter
                than this distance apart
        """
        if log.do_warning:
            log.warn("Currently, Yaff does not consider interactions of a guest molecule "
                     "with its periodic images in MC simulations. Make sure that you choose a system size "
                     "that is large compared to the guest dimensions, so it is indeed "
                     "acceptable to neglect these interactions.")
        with log.section(self.log_name), timer.section(self.log_name):
            # Initialization
            self.translation_stepsize = translation_stepsize
            self.volumechange_stepsize = volumechange_stepsize
            self.close_contact = close_contact
            if initial is not None:
                self.N = initial.natom//self.guest.natom
                assert self.guest.natom*self.N==initial.natom, ("Initial configuration does not contain correct number of atoms")
                self.current_configuration = initial
                self.get_ff(self.N).system.pos[:] = initial.pos
                if self.ewald_reci is not None:
                    self.ewald_reci.compute_structurefactors(
                        initial.pos,
                        initial.charges,
                        self.ewald_reci.cosfacs, self.ewald_reci.sinfacs)
            else:
                self.current_configuration = self.get_ff(self.N).system
            self.energy = einit
            if not self.conditions_set:
                raise ValueError("External conditions have not been set!")
            # Normalized probabilities and accompanying methods specifying the trial MC moves
            # Trial moves are sorted alphabetically
            if mc_moves is None: mc_moves = self.default_trials
            trials, probabilities = [], []
            for t in sorted(mc_moves.keys()):
                if not t in self.allowed_trials:
                    raise ValueError("Trial move %s not allowed!"%t)
                trial = getattr(mctrials,"Trial"+t.capitalize(),None)
                if trial is None:
                    raise NotImplementedError("The requested trial move %s is not implemented"%(t))
                # Trials is a list containing instances of Trial classes from the mctrials module
                trials.append(trial(self))
                probabilities.append(mc_moves[t])
            probabilities = np.asarray(probabilities)
            probabilities /= np.sum(probabilities)
            assert np.all(probabilities>=0.0), "Negative probabilities are not allowed!"
            # Take the cumulative sum, makes it a bit easier to determine which MC move is selected
            probabilities = np.cumsum(probabilities)
            # Array to keep track of accepted (1st column) and tried (2nd column)
            # moves, with rows corresponding to different possible moves
            acceptance = np.zeros((len(trials),2), dtype=int)
            # Start performing MC moves
            self.Nmean = self.N
            self.emean = self.energy
            self.Vmean = self.current_configuration.cell.volume
            self.counter = 0
            self.call_hooks()
            for istep in range(nsteps):
                switch = np.random.rand()
                # Select one of the possible MC moves
                imove = np.where(switch<probabilities)[0][0]
                # Call the corresponding method
                accepted = trials[imove]()
                # Update records with accepted and tried MC moves
                acceptance[imove,1] += 1
                if accepted: acceptance[imove,0] += 1
                self.counter += 1
                self.Nmean += (self.N-self.Nmean)/self.counter
                self.emean += (self.energy-self.emean)/self.counter
                self.Vmean += (self.current_configuration.cell.volume-self.Vmean)/self.counter
                self.call_hooks()
            return acceptance
예제 #27
0
파일: lammpsio.py 프로젝트: mcoolsce/yaff
def write_lammps_system_data(system,
                             ff=None,
                             fn='lammps.data',
                             triclinic=True):
    '''
        Write information about a Yaff system to a LAMMPS data file
        Following information is written: cell vectors, atom type ids
        and topology (as defined by the bonds)

        **Arguments**
            system
                Yaff system

            fn
                Filename to write the LAMMPS data to

            triclinic
                Boolean, specify whether a triclinic cell will be used during
                the simulation. If the cell is orthogonal, set it to False
                as LAMMPS should run slightly faster.
                Default: True
    '''
    if system.cell.nvec != 3:
        raise ValueError(
            'The system must be 3D periodic for Lammps calculations.')
    if system.ffatypes is None:
        raise ValueError('Atom types need to be defined.')
    if system.bonds is None:
        raise ValueError('Bonds need to be defined')
    if system.charges is None:
        if log.do_warning:
            log.warn(
                "System has no charges, writing zero charges to LAMMPS file")
        charges = np.zeros((system.natom, ))
    else:
        charges = system.charges
    if ff is None:
        ffatypes, ffatype_ids = system.ffatypes, system.ffatype_ids
    else:
        ffatypes, ffatype_ids = get_lammps_ffatypes(ff)
    fdat = open(fn, 'w')
    fdat.write(
        "Generated by Yaff\n\n%20d atoms\n%20d bonds\n%20d angles \n%20d dihedrals\n%20d impropers\n\n"
        % (system.natom, system.nbond, 0, 0, 0))
    fdat.write(
        "%20d atom types\n%20d bond types\n%20d angle types\n%20d dihedral types\n%20d improper types\n\n"
        % (np.amax(ffatype_ids) + 1, 1, 0, 0, 0))
    rvecs, R = cell_lower(system.cell.rvecs)
    pos = np.einsum('ij,kj', system.pos, R)
    fdat.write(
        "%30.24f %30.24f xlo xhi\n%30.24f %30.24f ylo yhi\n%30.24f %30.24f zlo zhi\n"
        % (0.0, rvecs[0, 0], 0.0, rvecs[1, 1], 0.0, rvecs[2, 2]))
    if triclinic:
        fdat.write("%30.24f %30.24f %30.24f xy xz yz\n" %
                   (rvecs[1, 0], rvecs[2, 0], rvecs[2, 1]))
    fdat.write("Atoms\n\n")
    for i in range(system.natom):
        fdat.write("%5d %3d %3d %30.24f %30.24f %30.24f %30.24f\n" %
                   (i + 1, 1, ffatype_ids[i] + 1, charges[i], pos[i, 0],
                    pos[i, 1], pos[i, 2]))
    fdat.write("\nBonds\n\n")
    for i in range(system.nbond):
        fdat.write("%5d %3d %5d %5d\n" %
                   (i + 1, 1, system.bonds[i, 0] + 1, system.bonds[i, 1] + 1))
    fdat.close()
예제 #28
0
파일: lammpsio.py 프로젝트: mcoolsce/yaff
def write_lammps_table(ff,
                       fn='lammps.table',
                       rmin=0.50 * angstrom,
                       nrows=2500,
                       unit_style='electron'):
    '''
       Write tables containing noncovalent interactions for LAMMPS.
       For every pair of ffatypes, a separate table is generated.
       Because electrostatic interactions require a specific treatment, point-
       charge electrostatics are NOT included in the tables.

       When distributed charge distributions (e.g. Gaussian) are used, this
       complicates matters. LAMMPS will still only treat point-charge
       electrostatics using a dedicated method (e.g. Ewald or PPPM), so the
       table has to contain the difference between the distributed charges and
       the point charge electrostatic interactions. This also means that every
       ffatype need a unique charge distribution, i.e. all atoms of the same
       atom type need to have the same charge and Gaussian radius.

       All pair potentials contributing to the table need to have the same
       scalings for near-neighbor interactions; this is however independent
       of the generation of the table and is dealt with elsewhere

       **Arguments:**

       ff
            Yaff ForceField instance

       **Optional arguments:**

       fn
            Filename where tables will be stored

    '''
    # Find out if we are dealing with electrostatics from distributed charges
    corrections = []
    for part in ff.parts:
        if part.name == 'pair_ei':
            if np.any(part.pair_pot.radii != 0.0):
                # Create a ForcePart with electrostatics from distributed
                # charges, completely in real space.
                pair_pot_dist = PairPotEI(part.pair_pot.charges,
                                          0.0,
                                          part.pair_pot.rcut,
                                          tr=part.pair_pot.get_truncation(),
                                          dielectric=part.pair_pot.dielectric,
                                          radii=part.pair_pot.radii)
                fp_dist = ForcePartPair(ff.system, ff.nlist, part.scalings,
                                        pair_pot_dist)
                corrections.append((fp_dist, 1.0))
                # Create a ForcePart with electrostatics from point
                # charges, completely in real space.
                pair_pot_point = PairPotEI(part.pair_pot.charges,
                                           0.0,
                                           part.pair_pot.rcut,
                                           tr=part.pair_pot.get_truncation(),
                                           dielectric=part.pair_pot.dielectric)
                fp_point = ForcePartPair(ff.system, ff.nlist, part.scalings,
                                         pair_pot_point)
                corrections.append((fp_point, -1.0))
    # Find the largest cut-off
    rmax = 0.0
    for part in ff.parts:
        if part.name.startswith('pair_'):
            if part.name == 'pair_ei' and len(corrections) == 0: continue
            rmax = np.amax([rmax, part.pair_pot.rcut])
    # Get LAMMPS ffatypes
    ffatypes, ffatype_ids = get_lammps_ffatypes(ff)
    # Select atom pairs for each pair of atom types
    ffa_pairs = []
    for i in range(len(ffatypes)):
        index0 = np.where(ffatype_ids == i)[0][0]
        for j in range(i, len(ffatypes)):
            index1 = -1
            candidates = np.where(ffatype_ids == j)[0]
            for cand in candidates:
                if cand==index0 or cand in ff.system.neighs1[index0] or\
                    cand in ff.system.neighs2[index0] or cand in ff.system.neighs3[index0] or\
                    cand in ff.system.neighs4[index0]:
                    continue
                else:
                    index1 = cand
                    break
            if index1 == -1:
                log("ERROR constructing LAMMPS tables: there is no pair of atom types %s-%s which are not near neighbors"
                    % (ffatypes[i], ffatypes[j]))
                log("Consider using a supercell to fix this problem")
                raise ValueError
            ffa_pairs.append([index0, index1])
    if log.do_medium:
        with log.section('LAMMPS'):
            log("Generating LAMMPS table with covalent interactions")
            log.hline()
            log("rmin = %s | rmax = %s" % (log.length(rmin), log.length(rmax)))
    # We only consider one neighbor interaction
    ff.compute()
    ff.nlist.nneigh = 1
    # Construct array of evenly spaced values
    distances = np.linspace(rmin, rmax, nrows)
    ftab = open(fn, 'w')
    ftab.write("# LAMMPS tabulated potential generated by Yaff\n")
    ftab.write("# All quantities in atomic units\n")
    ftab.write(
        "# The names of the tables refer to the ffatype_ids that have to be used in the Yaff system\n"
    )
    ftab.write("#%4s %13s %21s %21s\n" % ("i", "d", "V", "F"))
    # Loop over all atom pairs
    for index0, index1 in ffa_pairs:
        energies = []
        for d in distances:
            gposnn = np.zeros(ff.system.pos.shape, float)
            ff.nlist.neighs[0] = (index0, index1, d, 0.0, 0.0, d, 0, 0, 0)
            energy = 0.0
            for part in ff.parts:
                if not part.name.startswith('pair'): continue
                if part.name == 'pair_ei': continue
                energy += part.compute(gpos=gposnn)
            for part, sign in corrections:
                gposcorr = np.zeros(ff.system.pos.shape, float)
                energy += sign * part.compute(gpos=gposcorr)
                gposnn[:] += sign * gposcorr
            row = [d, energy, gposnn[index0, 2]]
            energies.append(row)
        energies = np.asarray(energies)
        ffai = ffatypes[ffatype_ids[index0]]
        ffaj = ffatypes[ffatype_ids[index1]]
        if np.all(energies[:, 1] == 0.0):
            log.warn("Noncovalent energies between atoms %d (%s) and %d (%s) are zero"\
                    % (index0,ffai,index1,ffaj))
        if np.all(energies[:, 2] == 0.0):
            log.warn("Noncovalent forces between atoms %d (%s) and %d (%s) are zero"\
                    % (index0,ffai,index1,ffaj))
        if ffai > ffaj:
            name = '%s---%s' % (str(ffai), str(ffaj))
        else:
            name = '%s---%s' % (str(ffaj), str(ffai))
        ftab.write("%s\nN %d R %13.8f %13.8f\n\n" %
                   (name, nrows, rmin / lammps_units[unit_style]['distance'],
                    rmax / lammps_units[unit_style]['distance']))
        for irow, row in enumerate(energies):
            ftab.write(
                "%05d %+13.8f %+21.12f %+21.12f\n" %
                (irow + 1, row[0] / lammps_units[unit_style]['distance'],
                 row[1] / lammps_units[unit_style]['energy'],
                 row[2] / lammps_units[unit_style]['energy'] *
                 lammps_units[unit_style]['distance']))
        if log.do_medium:
            log("%s done" % name)
예제 #29
0
 def _init_derived_ffatypes(self):
     if self.ffatype_ids is None:
         if len(self.ffatypes) != self.natom:
             raise TypeError(
                 'When the ffatype_ids are derived automatically, the length of the ffatypes list must match the number of atoms.'
             )
         lookup = {}
         ffatypes = []
         self.ffatype_ids = np.zeros(self.natom, int)
         for i in range(self.natom):
             if self.scope_ids is None:
                 ffatype = self.ffatypes[i]
                 key = ffatype, None
             else:
                 scope_id = self.scope_ids[i]
                 ffatype = self.ffatypes[i]
                 key = ffatype, scope_id
             ffatype_id = lookup.get(key)
             if ffatype_id is None:
                 ffatype_id = len(ffatypes)
                 ffatypes.append(ffatype)
                 lookup[key] = ffatype_id
             self.ffatype_ids[i] = ffatype_id
         self.ffatypes = ffatypes
     for ffatype in self.ffatypes:
         check_name(ffatype)
     # check the range of the ids
     if self.ffatype_ids.min() != 0 or self.ffatype_ids.max() != len(
             self.ffatypes) - 1:
         raise ValueError('The ffatype_ids have incorrect bounds.')
     # differentiate ffatype_ids if the same ffatype_id is used in different
     # scopes
     if self.scopes is not None:
         self.ffatype_id_to_scope_id = {}
         fixed_fids = {}
         for i in range(self.natom):
             fid = self.ffatype_ids[i]
             sid = self.ffatype_id_to_scope_id.get(fid)
             if sid is None:
                 self.ffatype_id_to_scope_id[fid] = self.scope_ids[i]
             elif sid != self.scope_ids[i]:
                 # We found the same ffatype_id in a different scope_id. This
                 # must be fixed. First check if we have already a new
                 # scope_id ready
                 sid = self.scope_ids[i]
                 new_fid = fixed_fids.get((sid, fid))
                 if new_fid is None:
                     # No previous new fid create, do it now.
                     new_fid = len(self.ffatypes)
                     # Copy the ffatype label
                     self.ffatypes.append(self.ffatypes[fid])
                     # Keep track of the new fid
                     fixed_fids[(sid, fid)] = new_fid
                     if log.do_warning:
                         log.warn(
                             'Atoms with type ID %i in scope %s were changed to type ID %i.'
                             % (fid, self.scopes[sid], new_fid))
                 # Apply the new fid
                 self.ffatype_ids[i] = new_fid
                 self.ffatype_id_to_scope_id[new_fid] = sid
     # Turn the ffatypes in the scopes into array
     if self.ffatypes is not None:
         self.ffatypes = np.array(self.ffatypes, copy=False)
     if self.scopes is not None:
         self.scopes = np.array(self.scopes, copy=False)
     # check the range of the ids
     if self.ffatype_ids.min() != 0 or self.ffatype_ids.max() != len(
             self.ffatypes) - 1:
         raise ValueError('The ffatype_ids have incorrect bounds.')
     if log.do_medium:
         log('The following atom types are present in the system:')
         log.hline()
         if self.scopes is None:
             log('             Atom type   ID   Number of atoms')
             log.hline()
             for ffatype_id, ffatype in enumerate(self.ffatypes):
                 log('%22s  %3i       %3i' %
                     (ffatype, ffatype_id,
                      (self.ffatype_ids == ffatype_id).sum()))
         else:
             log('                 Scope              Atom type   ID   Number of atoms'
                 )
             log.hline()
             for ffatype_id, ffatype in enumerate(self.ffatypes):
                 scope = self.scopes[
                     self.ffatype_id_to_scope_id[ffatype_id]]
                 log('%22s %22s  %3i       %3i' %
                     (scope, ffatype, ffatype_id,
                      (self.ffatype_ids == ffatype_id).sum()))
         log.hline()
         log.blank()
예제 #30
0
파일: scaling.py 프로젝트: tovrstra/yaff
    def check_mic(self, system):
        '''Check if each scale2 and scale3 are uniquely defined.

           **Arguments:**

           system
                An instance of the system class, i.e. the one that is used to
                create this scaling object.

           This check is done by constructing for each scaled pair, all possible
           bond paths between the two atoms. For each path, the bond vectors
           (after applying the minimum image convention) are added. If for a
           given pair, these sums of bond vectors differ between all possible
           paths, the differences are expanded in cell vectors which can be used
           to construct a proper supercell in which scale2 and scale3 pairs are
           all uniquely defined.
        '''
        if system.cell.nvec == 0:
            return
        troubles = False
        with log.section('SCALING'):
            for i0, i1, scale, nbond in self.stab:
                if nbond == 1:
                    continue
                all_deltas = []
                paths = []
                for path in iter_paths(system, i0, i1, nbond):
                    delta_total = 0
                    for j0 in xrange(nbond):
                        j1 = j0 + 1
                        delta = system.pos[path[j0]] - system.pos[path[j1]]
                        system.cell.mic(delta)
                        delta_total += delta
                    all_deltas.append(delta_total)
                    paths.append(path)
                all_deltas = np.array(all_deltas)
                if abs(all_deltas.mean(axis=0) - all_deltas).max() > 1e-10:
                    troubles = True
                    if log.do_warning:
                        log.warn('Troublesome pair scaling detected.')
                    log('The following bond paths connect the same pair of '
                        'atoms, yet the relative vectors are different.')
                    for ipath in xrange(len(paths)):
                        log('%2i %27s %10s %10s %10s' % (
                            ipath,
                            ','.join(str(index) for index in paths[ipath]),
                            log.length(all_deltas[ipath,0]),
                            log.length(all_deltas[ipath,1]),
                            log.length(all_deltas[ipath,2]),
                        ))
                    log('Differences between relative vectors in fractional '
                        'coordinates:')
                    for ipath0 in xrange(1, len(paths)):
                        for ipath1 in xrange(ipath0):
                            diff = all_deltas[ipath0] - all_deltas[ipath1]
                            diff_frac = np.dot(system.cell.gvecs, diff)
                            log('%2i %2i %10.4f %10.4f %10.4f' % (
                                ipath0, ipath1,
                                diff_frac[0], diff_frac[1], diff_frac[2]
                            ))
                    log.blank()
        if troubles:
            raise AssertionError('Due to the small spacing between some crystal planes, the scaling of non-bonding interactions will not work properly. Use a supercell to avoid this problem.')
예제 #31
0
파일: xyz.py 프로젝트: boegel/yaff
def xyz_to_hdf5(f, fn_xyz, sub=slice(None), file_unit=angstrom, name='pos'):
    """Convert XYZ trajectory file to Yaff HDF5 format.

       **Arguments:**

       f
            An open and writable HDF5 file.

       fn_xyz
            The filename of the XYZ trajectory file.

       **Optional arguments:**

       sub
            The sub argument for the XYZReader. This must be a slice object that
            defines the subsampling of the XYZ file reader. By default all
            frames are read.

       file_unit
            The unit of the data in the XYZ file. [default=angstrom]

       name
            The name of the HDF5 dataset where the trajectory is stored. This
            array is stored in the 'trajectory' group.

       This routine will also test the consistency of the row attribute of the
       trajectory group. If some trajectory data is already present, it will be
       replaced by the new data. It is highly recommended to first initialize
       the HDF5 file with the ``to_hdf5`` method of the System class.
    """
    with log.section('XYZH5'):
        if log.do_medium:
            log('Loading XYZ file \'%s\' into \'trajectory/%s\' of HDF5 file \'%s\''
                % (fn_xyz, name, f.filename))

        # First make sure the HDF5 file has a system description that is consistent
        # with the XYZ file.
        if 'system' not in f:
            raise ValueError('The HDF5 file must contain a system group.')
        if 'numbers' not in f['system']:
            raise ValueError(
                'The HDF5 file must have a system group with atomic numbers.')

        xyz_reader = XYZReader(fn_xyz, sub=sub, file_unit=file_unit)
        if len(xyz_reader.numbers) != len(f['system/numbers']):
            raise ValueError(
                'The number of atoms in the HDF5 and the XYZ files does not match.'
            )
        if (xyz_reader.numbers != f['system/numbers']).any():
            log.warn(
                'The atomic numbers of the HDF5 and XYZ file do not match.')

        # Take care of the trajectory group
        tgrp = get_trajectory_group(f)

        # Take care of the dataset
        ds, = get_trajectory_datasets(tgrp,
                                      (name, (len(xyz_reader.numbers), 3)))

        # Fill the dataset with data.
        row = get_last_trajectory_row([ds])
        for title, coordinates in xyz_reader:
            write_to_dataset(ds, coordinates, row)
            row += 1

        # Check number of rows
        check_trajectory_rows(tgrp, [ds], row)
예제 #32
0
파일: xyz.py 프로젝트: molmod/yaff
def xyz_to_hdf5(f, fn_xyz, sub=slice(None), file_unit=angstrom, name='pos'):
    """Convert XYZ trajectory file to Yaff HDF5 format.

       **Arguments:**

       f
            An open and writable HDF5 file.

       fn_xyz
            The filename of the XYZ trajectory file.

       **Optional arguments:**

       sub
            The sub argument for the XYZReader. This must be a slice object that
            defines the subsampling of the XYZ file reader. By default all
            frames are read.

       file_unit
            The unit of the data in the XYZ file. [default=angstrom]

       name
            The name of the HDF5 dataset where the trajectory is stored. This
            array is stored in the 'trajectory' group.

       This routine will also test the consistency of the row attribute of the
       trajectory group. If some trajectory data is already present, it will be
       replaced by the new data. It is highly recommended to first initialize
       the HDF5 file with the ``to_hdf5`` method of the System class.
    """
    with log.section('XYZH5'):
        if log.do_medium:
            log('Loading XYZ file \'%s\' into \'trajectory/%s\' of HDF5 file \'%s\'' % (
                fn_xyz, name, f.filename
            ))

        # First make sure the HDF5 file has a system description that is consistent
        # with the XYZ file.
        if 'system' not in f:
            raise ValueError('The HDF5 file must contain a system group.')
        if 'numbers' not in f['system']:
            raise ValueError('The HDF5 file must have a system group with atomic numbers.')

        xyz_reader = XYZReader(fn_xyz, sub=sub, file_unit=file_unit)
        if len(xyz_reader.numbers) != len(f['system/numbers']):
            raise ValueError('The number of atoms in the HDF5 and the XYZ files does not match.')
        if (xyz_reader.numbers != f['system/numbers']).any():
            log.warn('The atomic numbers of the HDF5 and XYZ file do not match.')

        # Take care of the trajectory group
        tgrp = get_trajectory_group(f)

        # Take care of the dataset
        ds, = get_trajectory_datasets(tgrp, (name, (len(xyz_reader.numbers), 3)))

        # Fill the dataset with data.
        row = get_last_trajectory_row([ds])
        for title, coordinates in xyz_reader:
            write_to_dataset(ds, coordinates, row)
            row += 1

        # Check number of rows
        check_trajectory_rows(tgrp, [ds], row)
예제 #33
0
def g09log_to_hdf5(f, fn_log):
    """Convert Gaussian09 BOMD log file to Yaff HDF5 format.

       **Arguments:**

       f
            An open and writable HDF5 file.

       fn_log
            The name of the Gaussian log file.
    """
    with log.section('G09H5'):
        if log.do_medium:
            log('Loading Gaussian 09 file \'%s\' into \'trajectory\' of HDF5 file \'%s\''
                % (fn_log, f.filename))

        # First make sure the HDF5 file has a system description that is consistent
        # with the XYZ file.
        if 'system' not in f:
            raise ValueError('The HDF5 file must contain a system group.')
        if 'numbers' not in f['system']:
            raise ValueError(
                'The HDF5 file must have a system group with atomic numbers.')
        natom = f['system/numbers'].shape[0]

        # Take care of the trajectory group
        tgrp = get_trajectory_group(f)

        # Take care of the pos and vel datasets
        dss = get_trajectory_datasets(
            tgrp,
            ('pos', (natom, 3)),
            ('vel', (natom, 3)),
            ('frc', (natom, 3)),
            ('time', (1, )),
            ('step', (1, )),
            ('epot', (1, )),
            ('ekin', (1, )),
            ('etot', (1, )),
        )
        ds_pos, ds_vel, ds_frc, ds_time, ds_step, ds_epot, ds_ekin, ds_etot = dss

        # Load frame by frame
        row = get_last_trajectory_row(dss)
        for numbers, pos, vel, frc, time, step, epot, ekin, etot in _iter_frames_g09(
                fn_log):
            if (numbers != f['system/numbers']).any():
                log.warn(
                    'The element numbers of the HDF5 and LOG file do not match.'
                )
            write_to_dataset(ds_pos, pos, row)
            write_to_dataset(ds_vel, vel, row)
            write_to_dataset(ds_frc, frc, row)
            write_to_dataset(ds_time, time, row)
            write_to_dataset(ds_step, step, row)
            write_to_dataset(ds_epot, epot, row)
            write_to_dataset(ds_ekin, ekin, row)
            write_to_dataset(ds_etot, etot, row)
            row += 1

        # Check number of rows
        check_trajectory_rows(tgrp, dss, row)
예제 #34
0
    def check_mic(self, system):
        '''Check if each scale2 and scale3 are uniquely defined.

           **Arguments:**

           system
                An instance of the system class, i.e. the one that is used to
                create this scaling object.

           This check is done by constructing for each scaled pair, all possible
           bond paths between the two atoms. For each path, the bond vectors
           (after applying the minimum image convention) are added. If for a
           given pair, these sums of bond vectors differ between all possible
           paths, the differences are expanded in cell vectors which can be used
           to construct a proper supercell in which scale2 and scale3 pairs are
           all uniquely defined.
        '''
        if system.cell.nvec == 0:
            return
        troubles = False
        with log.section('SCALING'):
            for i0, i1, scale, nbond in self.stab:
                if nbond == 1:
                    continue
                all_deltas = []
                paths = []
                for path in iter_paths(system, i0, i1, nbond):
                    delta_total = 0
                    for j0 in range(nbond):
                        j1 = j0 + 1
                        delta = system.pos[path[j0]] - system.pos[path[j1]]
                        system.cell.mic(delta)
                        delta_total += delta
                    all_deltas.append(delta_total)
                    paths.append(path)
                all_deltas = np.array(all_deltas)
                if abs(all_deltas.mean(axis=0) - all_deltas).max() > 1e-10:
                    troubles = True
                    if log.do_warning:
                        log.warn('Troublesome pair scaling detected.')
                    log('The following bond paths connect the same pair of '
                        'atoms, yet the relative vectors are different.')
                    for ipath in range(len(paths)):
                        log('%2i %27s %10s %10s %10s' % (
                            ipath,
                            ','.join(str(index) for index in paths[ipath]),
                            log.length(all_deltas[ipath, 0]),
                            log.length(all_deltas[ipath, 1]),
                            log.length(all_deltas[ipath, 2]),
                        ))
                    log('Differences between relative vectors in fractional '
                        'coordinates:')
                    for ipath0 in range(1, len(paths)):
                        for ipath1 in range(ipath0):
                            diff = all_deltas[ipath0] - all_deltas[ipath1]
                            diff_frac = np.dot(system.cell.gvecs, diff)
                            log('%2i %2i %10.4f %10.4f %10.4f' %
                                (ipath0, ipath1, diff_frac[0], diff_frac[1],
                                 diff_frac[2]))
                    log.blank()
        if troubles:
            raise AssertionError(
                'Due to the small spacing between some crystal planes, the scaling of non-bonding interactions will not work properly. Use a supercell to avoid this problem.'
            )