Exemple #1
0
def screened_effective_charge(born, eps):
   """
   Compute screened effective charge tensor from Born and dielectric tensors
   """

   screened = fzeros((3,3,3,3))
   for i in frange(3):
      for j in frange(3):
         for k in frange(3):
            for l in frange(3):
               if eps[k,l] == 0.0: continue
               screened[i,j,k,l] = born[i,j]/np.sqrt(eps[k,l])

   return screened
Exemple #2
0
    def __getitem__(self, i):
        if not self.initialised:
            if self is self.parent.hysteretic_connect:
                self.calc_connect_hysteretic(self.parent)
            else:
                self.calc_connect(self.parent)

        distance = farray(0.0)
        diff = fzeros(3)
        cosines = fzeros(3)
        shift = fzeros(3, dtype=np.int32)

        res = []
        if not get_fortran_indexing():
            i = i + 1  # convert to 1-based indexing

        for n in frange(self.n_neighbours(i)):
            j = self.neighbour(self.parent, i, n, distance, diff, cosines,
                               shift)
            if not get_fortran_indexing():
                j = j - 1
            res.append(NeighbourInfo(j, distance, diff, cosines, shift))

        if get_fortran_indexing():
            res = farray(res)  # to give 1-based indexing
        return res
Exemple #3
0
def get_bond_lengths(at):
    """Return a dictionary mapping tuples (Species1, Species2) to an farray of bond-lengths"""
    at.calc_connect()
    r_ij = farray(0.0)
    res = {}
    for i in frange(at.n):
        for n in frange(at.n_neighbours(i)):
            j = at.neighbour(i, n, distance=r_ij)
            print i, j, at.z[i], at.z[j], r_ij
            minij, maxij = min((i, j)), max((i, j))
            key = (at.species[minij].stripstrings(),
                   at.species[maxij].stripstrings())
            if not key in res: res[key] = []
            res[key].append(r_ij.astype(float))
    print res
    return dict((k, farray(v)) for (k, v) in res.iteritems())
Exemple #4
0
def epsilon_infty(pot, at, deltafield=0.001, zerotol=1e-5):
   """
   Calculate dielectric tensor.

   Potential must be polarisable and allow an external electic field to be applied.
   """

   dielectric_tensor = fzeros((3,3))
   celldip = fzeros((3,3))

   # Calculation with no applied field
   pot.calc(at, force=True, restart=True, applied_efield=False)
   celldip0 = at.dipoles.sum(axis=2)/BOHR

   at.add_property('ext_efield', 0., n_cols=3)

   # Now we apply field along each of x,y,z in turn, and
   # calculate overall cell dipole moment
   for i in frange(3):

      at.ext_efield[:] = 0.0
      at.ext_efield[i,:] += deltafield/(BOHR/HARTREE)

      pot.calc(at, force=True, applied_efield=True)
      celldip[:,i] = at.dipoles.sum(axis=2)/BOHR
      dielectric_tensor[:,i] = celldip[:,i] - celldip0
      
   dielectric_tensor = 4.0*PI*dielectric_tensor/deltafield/(at.cell_volume()/BOHR**3) + fidentity(3)
   at.ext_efield[:] = 0.0

   dielectric_tensor[dielectric_tensor < zerotol] = 0.0

   return dielectric_tensor
Exemple #5
0
def calc_effective_charge_vectors(a, born_tensor):

   effective_charge = fzeros(3)
   for i in frange(a.n):
      p_norm = a.phonon[i]/np.sqrt(np.dot(a.phonon[i],a.phonon[i]))
      disp = (p_norm/np.sqrt(ElementMass[str(a.species[i])]*MASSCONVERT))/BOHR
      print disp
      effective_charge = effective_charge + dot(born_tensor[:,:,i], disp)

   return effective_charge
Exemple #6
0
    def _indices(self):
        """Return array of atoms indices

        If global ``fortran_indexing`` is True, returns FortranArray containing
        numbers 1..self.n.  Otherwise, returns a standard numpuy array
        containing numbers in range 0..(self.n-1)."""

        if get_fortran_indexing():
            return farray(list(frange(len(self))))
        else:
            return np.array(list(range(len(self))))
Exemple #7
0
def find_atoms_within_cutoff(atoms, cutoff, d=0):
    tmp_atoms = Atoms(atoms, fortran_indexing=False)
    tmp_atoms.calc_connect()
    over = []
    rij = farray(0.0)
    for i in frange(len(tmp_atoms)):
        for n in frange(tmp_atoms.n_neighbours(i)):
            j = tmp_atoms.neighbour(i, n, distance=rij)
            # print j
            if rij < cutoff and i > j:
                if d == 0:
                    over.append(j - 1)
                else:
                    over.append(i - 1)

    # for i in range(len(atoms)): #python method, may be slower than fortran index?
    #     indices, offsets = tmp_atoms.neighbours.get_neighbors(i)
    #     for j, offset in zip(indices, offsets):
    #         diff=tmp_atoms.get_distance(i,j)
    # if diff < cutoff and i > j : #i>j is important for returning only one atom id
    #             over.append(j)
    return over
Exemple #8
0
def apply_cp2k_sort_order(at, rev_sort_index_filename='quip_rev_sort_index'):
    """Reorder atoms in `at` so that they match rev_sort_index read from file.

       Returns sort_index and rev_sort_index arrays."""

    fields = [int(x) for x in open(rev_sort_index_filename).read().split()]
    rev_sort_index = farray(fields, dtype=np.int32)
    rev_sort_index_copy = rev_sort_index.copy()
    sort_index = farray(frange(at.n), dtype=np.int32)
    insertion_sort(rev_sort_index_copy, sort_index)
    at.add_property('rev_sort_index', rev_sort_index)
    at.sort('rev_sort_index')

    return sort_index, rev_sort_index
Exemple #9
0
def born_effective_charge(pot, at0, dx=1e-5, args_str=None):
   """
   Calculate Born effective charges for all atoms in at0

   Potential must be polarizable, i.e. compute dipole moments.
   """

   born_tensor = fzeros((3,3,at0.n))

   restart = True
   for i in frange(at0.n):
      for j in frange(3):

         at = at0.copy()
         at.pos[j,i] -= dx
         at.calc_connect()

         pot.calc(at, force=True, restart=restart, args_str=args_str)
         restart = True

         dip1 = fzeros(3)
         for k in frange(at.n):
            dip1 += at.dipoles[k] + at.charge[k]*at.pos[:,k]

         at = at0.copy()
         at.pos[j,i] += dx
         at.calc_connect()

         pot.calc(at, force=True, restart=restart, args_str=args_str)
         
         dip2 = fzeros(3)
         for k in frange(at.n):
            dip2 += at.dipoles[k] + at.charge[k]*at.pos[:,k]

         born_tensor[:,j,i] = (dip2 - dip1)/(dx*2.0)

   return born_tensor
Exemple #10
0
def force_test(at, p, dx=1e-4):
    """
    Compare analyric and numeric forces for the Potential `p` with Atoms `at`

    Finite difference derivates are calculated by moving each atom by `dx`.
    """
    analytic_f = fzeros((3, at.n))
    p.calc(at, force=analytic_f)
    num_f = fzeros((3, at.n))
    ep, em = farray(0.0), farray(0.0)

    for i in frange(at.n):
        for j in (1, 2, 3):
            ap = at.copy()
            ap.pos[j, i] += dx
            p.calc(ap, energy=ep)
            print 'e+', j, i, ep
            ap.pos[j, i] -= 2.0 * dx
            p.calc(ap, energy=em)
            print 'e-', j, i, em
            num_f[j, i] = -(ep - em) / (2 * dx)

    return analytic_f, num_f, analytic_f - num_f
Exemple #11
0
    def calc(self,
             at,
             energy=None,
             force=None,
             virial=None,
             local_energy=None,
             local_virial=None,
             args_str=None,
             error=None,
             **kwargs):

        clusters = []
        orig_label = self.label
        if not self.mm_local:
            # always submit the MM calc
            if self.test_mode:
                clusters.append(at)
            else:
                self.server.put(at,
                                0,
                                self.label,
                                force_restart=self.force_restart)
            self.label += 1

        do_qm = not self.get('method').startswith('lotf') or self.get(
            'lotf_do_qm')

        if do_qm:
            #print 'REGIONS', [ k for k in at.properties.keys() if re.match('hybrid_[0-9]+', k) ]
            n_region = len([
                k for k in at.properties.keys()
                if re.match('hybrid_[0-9]+', k)
            ])

            if self.get('calc_weights'):
                system_timer('create_hybrid_weights')
                # overall hybrid property is union of all the hybrids
                if not hasattr(at, 'hybrid'):
                    at.add_property('hybrid', 0)
                at.hybrid[:] = 0
                for i in frange(n_region):
                    hybrid = getattr(at, 'hybrid_%d' % i)
                    at.hybrid[hybrid ==
                              HYBRID_ACTIVE_MARK] = HYBRID_ACTIVE_MARK
                if not hasattr(at, 'hybrid_mark'):
                    at.add_property('hybrid_mark', at.hybrid)
                at.hybrid_mark[:] = 0
                at.hybrid_mark = at.hybrid
                create_hybrid_weights_args = self.cluster_args.copy()
                create_hybrid_weights_args['buffer_hops'] = 0
                create_hybrid_weights_args[
                    'transition_hops'] = 0  # ensure a fast exit
                # overall hybrid -> hybrid_mark, weight_region1
                create_hybrid_weights(
                    at,
                    args_str=quippy.util.args_str(create_hybrid_weights_args))
                system_timer('create_hybrid_weights')

            # make clusters and submit to QM clients
            system_timer('make_clusters')
            for i in frange(n_region):
                hybrid_name = 'hybrid_%d' % i
                hybrid_mark_name = 'hybrid_mark_%d' % i
                if self.get('calc_weights'):
                    hybrid = getattr(at, hybrid_name)
                    if not hasattr(at, hybrid_mark_name):
                        at.add_property(hybrid_mark_name, HYBRID_NO_MARK)
                    hybrid_mark = getattr(at, hybrid_mark_name)

                    # set marks to allow previously active atoms to become buffer atoms
                    # create_hybrid_weights will then set the buffer marks
                    hybrid_mark[hybrid_mark ==
                                HYBRID_ACTIVE_MARK] = HYBRID_BUFFER_MARK
                    hybrid_mark[hybrid ==
                                HYBRID_ACTIVE_MARK] = HYBRID_ACTIVE_MARK

                    print('region %d, sum(hybrid) %d, sum(hybrid_mark) %d' %
                          (i, sum(hybrid), sum(hybrid_mark)))

                    create_hybrid_weights_args = self.cluster_args.copy()
                    create_hybrid_weights_args['run_suffix'] = '_%d' % i
                    create_hybrid_weights_args_str = quippy.util.args_str(
                        create_hybrid_weights_args)
                    print 'calling create_hybrid_weights with args_str %s' % create_hybrid_weights_args_str
                    create_hybrid_weights(
                        at, args_str=create_hybrid_weights_args_str)

                cluster_args_str = quippy.util.args_str(self.cluster_args)
                print 'calling create_cluster_simple with args_str %s' % cluster_args_str
                c = create_cluster_simple(at,
                                          mark_name=hybrid_mark_name,
                                          args_str=cluster_args_str)

                client_id = i
                if self.mm_local:
                    client_id -= 1
                if self.save_clusters:
                    c.write(
                        os.path.join(
                            self.rundir, 'cluster.client-%03d.label-%04d.xyz' %
                            (client_id, self.label)))
                if self.test_mode:
                    clusters.append(c)
                else:
                    self.server.put(c,
                                    client_id,
                                    self.label,
                                    force_restart=self.force_restart)
                self.label += 1
            system_timer('make_clusters')

        # wait for results to be ready
        system_timer('get_results')
        if self.test_mode:
            results = []
            for i, cluster in enumerate(clusters):
                result = cluster.copy()
                result.set_cutoff(self.qm_pot.cutoff())
                result.calc_connect()
                self.qm_pot.calc(result, force=True)
                result.params['label'] = orig_label + i
                results.append(result)
        else:
            results = self.server.get_results()
        system_timer('get_results')

        system_timer('process_results')
        if self.mm_local:
            qm_results = results
        else:
            # process MM results
            mm_results, qm_results = results[:1], results[1:]
            mm_result = mm_results[0]
            at.add_property('mm_force', mm_result.force, overwrite=True)

        if do_qm:
            # process QM results
            at.add_property('qm_force', 0., n_cols=3, overwrite=True)

            # extract forces from each cluster
            for i, r in fenumerate(qm_results):
                #print 'qm forces (orig order?):'
                #print '\n'.join(['%d: pos=%s f=%s' % (j, p, f) for j, p, f in zip(r.index, r.pos, r.force)])

                client_id = i
                if self.mm_local:
                    client_id -= 1
                if self.save_clusters:
                    r.write(
                        os.path.join(
                            self.rundir, 'results.client-%03d.label-%04d.xyz' %
                            (client_id, r.label)))

                mark_name = 'hybrid_mark_%d' % i
                mask = getattr(r, mark_name) == HYBRID_ACTIVE_MARK
                #print 'Cluster %d: QM forces on atoms %s' % (i, r.index[mask])
                #print r.force[:, mask].T
                # HL if set_fortran is false we need to reduce the index here because
                # the atoms object is expecting python indexing.
                at.qm_force[:,
                            [ind - 1
                             for ind in list(r.index[mask])]] = r.force[:,
                                                                        mask]

        system_timer('process_results')

        # now call the parent calc() to do the force mixing etc.
        force_mixing_args = kwargs.copy()
        force_mixing_args.update(self.cluster_args)
        force_mixing_args['calc_weights'] = False  # already done this above
        #print 'calling ForceMixingPotential.calc() with args %r' % force_mixing_args
        ForceMixingPotential.calc(self, at, energy, force, virial,
                                  local_energy, local_virial, args_str, error,
                                  **force_mixing_args)
Exemple #12
0
    def write(self, at):

        # find numbers of atoms of each type, property name and value to match, and property labels to print above numbers
        atnums = []
        prop_vals = []
        prop_vals_map = {}
        labels = []
        if (hasattr(self, 'species_list')):
            if (not hasattr(at, 'species')):
                sys.stderr.write(
                    "VASP writer needs species property when species_list is specified"
                )
                sys.exit(1)
            property = 'species'
            for s in self.species_list:
                labels.append(s)
                prop_vals.append(s)
                prop_vals_map[s] = 1
                atnums.append((at.species[:].stripstrings() == s).count())
        else:
            property = 'Z'
            atnums_map = {}
            for i_at in frange(at.n):
                try:
                    atnums_map["%d" % at.Z[i_at]] += 1
                except:
                    atnums_map["%d" % at.Z[i_at]] = 1

        for Z_s in sorted(atnums_map.keys(), key=lambda entry: int(entry)):
            prop_vals.append(int(Z_s))
            prop_vals_map[int(Z_s)] = 1
            labels.append(quippy.ElementName[int(Z_s)])
            atnums.append(int(atnums_map[Z_s]))

        # check that every atom has a valid type
        for i_at in frange(at.n):
            try:
                prop = getattr(at, property)[i_at].stripstrings()
            except:
                prop = getattr(at, property)[i_at]

            if not prop in prop_vals_map:
                # should probably handle situation when prop isn't a string, but that should never happen
                sys.stderr.write(
                    "Failed to find property %s in prop_vals_map dictionary" %
                    prop)
                sys.exit(1)

        swapped_a1_a2 = False
        vol = np.dot(at.lattice[:, 1],
                     np.cross(at.lattice[:, 2], at.lattice[:, 3]))
        if (vol < 0.0):
            t_a1 = at.lattice[:, 1].copy()
            at.lattice[:, 1] = at.lattice[:, 2]
            at.lattice[:, 2] = t_a1[:]
            swapped_a1_a2 = True
            sys.stderr.write(
                "WARNING: swapped a1 and a2 to get positive scalar triple product\n"
            )

        # Comment
        try:
            self.out.write(at.params['VASP_Comment'])
            if (swapped_a1_a2):
                self.out.write(
                    " a1 and a2 swapped relative to input to get positive volume"
                )
            self.out.write("\n")
        except:
            try:
                self.out.write(at.params['comment'])
            except:
                self.out.write('')
            if (swapped_a1_a2):
                self.out.write(
                    " a1 and a2 swapped relative to input to get positive volume"
                )
            self.out.write("\n")

        # Lattice
        self.out.write("1.0\n")
        self.out.write("%.12f %.12f %.12f\n" %
                       (at.lattice[1, 1], at.lattice[2, 1], at.lattice[3, 1]))
        self.out.write("%.12f %.12f %.12f\n" %
                       (at.lattice[1, 2], at.lattice[2, 2], at.lattice[3, 2]))
        self.out.write("%.12f %.12f %.12f\n" %
                       (at.lattice[1, 3], at.lattice[2, 3], at.lattice[3, 3]))

        # Numbers of atoms and type labels
        self.out.write(" ".join(labels) + "\n")
        self.out.write(" ".join([("%d" % Z) for Z in atnums]) + "\n")
        self.out.write("Selective Dynamics\n")
        if (hasattr(at, 'VASP_Pos_Format')):
            self.out.write(at.params['VASP_Pos_Format'] + "\n")
            if ((at.params['VASP_Pos_Format'][0] == 'd'
                 or at.params['VASP_Pos_Format'][0] == 'D') and swapped_a1_a2):
                t_p1 = at.pos[1, :].copy()
                at.pos[1, :] = at.pos[2, :]
                at.pos[2, :] = t_p1[:]
        else:
            self.out.write("Cartesian\n")

        # Positions
        for i in range(len(prop_vals)):
            for i_at in frange(at.n):
                try:
                    match = getattr(
                        at, property)[i_at].stripstrings() == prop_vals[i]
                except:
                    match = getattr(at, property)[i_at] == prop_vals[i]
                if (match):
                    self.out.write(
                        "%.12f %.12f %.12f   T T T\n" %
                        (at.pos[1, i_at], at.pos[2, i_at], at.pos[3, i_at]))

        # Velocities
        if (hasattr(at, 'velo')):
            self.out.write("\n")
            for i in range(len(prop_vals)):
                for i_at in frange(at.n):
                    try:
                        match = getattr(
                            at, property)[i_at].stripstrings() == prop_vals[i]
                    except:
                        match = getattr(at, property)[i_at] == prop_vals[i]
                    if (match):
                        self.out.write("%f %f %f\n" %
                                       (at.velo[1, i_at], at.velo[2, i_at],
                                        at.velo[3, i_at]))
Exemple #13
0
def VASP_OUTCAR_Reader(outcar, species=None):
    """Read a configuration from a VASP OUTCAR file."""

    if (outcar == 'stdin' or outcar == '-'):
        p = sys.stdin
    else:
        p = open(outcar, 'r')

    re_comment = re.compile("\s*POSCAR:\s*(.+)")
    re_potcar = re.compile("\s*POTCAR:\s*\S+\s+(\S+)")
    re_n_atoms = re.compile("\s*ions per type =\s*((?:\d+\s*)*)")

    energy_i = -1
    at_i = -1
    lat_i = -1
    elements = []
    n_at = -1
    at_cur = None
    for lr in p:
        l = lr.rstrip()
        if (n_at <= 0):
            # parse header type things
            m = re_comment.match(l)
            if (m is not None):
                VASP_Comment = m.group(1)
                # print "got VASP_Comment '%s'" % VASP_Comment
            m = re_potcar.match(l)
            if (m is not None):
                elements.append(m.group(1))
            m = re_n_atoms.match(l)
            if (m is not None):
                # print "got ions per type, groups are:"
                # print m.groups()
                lat = fzeros((3, 3))
                n_types = [int(f) for f in m.group(1).split()]
                n_at = sum(n_types)
                at = Atoms(n=n_at, latttice=lat)
                i_at = 0
                for type_i in range(len(n_types)):
                    for j in range(n_types[type_i]):
                        i_at += 1
                        # print "set species of atom %d to '%s'" % (i_at, elements[type_i])
                        at.species[i_at] = elements[type_i]
                at.set_zs()
        else:
            # parse per-config lattice/pos/force
            if (l.find("direct lattice vectors") >= 0):
                at_cur = at.copy()
                lat_cur = fzeros((3, 3))
                lat_i = 1
            elif (lat_i >= 1 and lat_i <= 3):
                lat_cur[:, lat_i] = [
                    float(r) for r in l.replace("-", " -").split()[0:3]
                ]
                lat_i += 1
            elif (l.find("TOTAL-FORCE (eV/Angst)") >= 0):
                if (not hasattr(at_cur, "force")):
                    at_cur.add_property("force", 0.0, n_cols=3)
                at_i = 1
                p.next()
            elif (at_i >= 1 and at_i <= at_cur.n):
                pos_force = [
                    float(r) for r in l.replace("-", " -").split()[0:6]
                ]
                at_cur.pos[:, at_i] = pos_force[0:3]
                at_cur.force[:, at_i] = pos_force[3:6]
                at_i += 1
            elif (l.find("free  energy") >= 0):
                at_cur.params['Energy'] = float(l.split()[4])
                energy_i = 1
                p.next()
            elif (energy_i == 1):
                # print "energy(sigma->0) line"
                # print l.split()
                at_cur.params['Energy_sigma_to_zero'] = float(l.split()[6])
                energy_i += 1
                yield at_cur
            if (at_cur is not None and at_i == at_cur.n):
                at_cur.set_lattice(lat_cur, False)
                for i in frange(at_cur.n):
                    dr = at_cur.diff_min_image(i, at.pos[:, i])
                    at_cur.pos[:, i] = at.pos[:, i] - dr
Exemple #14
0
    def calc(self, at, energy=None, force=None, virial=None,
             local_energy=None, local_virial=None,
             args_str=None, error=None, **kwargs):

        clusters = []
        orig_label = self.label
        if not self.mm_local:
            # always submit the MM calc
            if self.test_mode:
                clusters.append(at)
            else:
                self.server.put(at, 0, self.label, force_restart=self.force_restart)
            self.label += 1
        
        do_qm = not self.get('method').startswith('lotf') or self.get('lotf_do_qm')
        
        if do_qm:
            #print 'REGIONS', [ k for k in at.properties.keys() if re.match('hybrid_[0-9]+', k) ]
            n_region = len([ k for k in at.properties.keys() if re.match('hybrid_[0-9]+', k) ])

            if self.get('calc_weights'):
                system_timer('create_hybrid_weights')
                # overall hybrid property is union of all the hybrids
                if not hasattr(at, 'hybrid'):
                    at.add_property('hybrid', 0)
                at.hybrid[:] = 0
                for i in frange(n_region):
                    hybrid = getattr(at, 'hybrid_%d' % i)
                    at.hybrid[hybrid == HYBRID_ACTIVE_MARK] = HYBRID_ACTIVE_MARK
                if not hasattr(at, 'hybrid_mark'):
                    at.add_property('hybrid_mark', at.hybrid)
                at.hybrid_mark[:] = 0
                at.hybrid_mark = at.hybrid
                create_hybrid_weights_args = self.cluster_args.copy()
                create_hybrid_weights_args['buffer_hops'] = 0
                create_hybrid_weights_args['transition_hops'] = 0 # ensure a fast exit
                # overall hybrid -> hybrid_mark, weight_region1
                create_hybrid_weights(at, args_str=quippy.util.args_str(create_hybrid_weights_args))
                system_timer('create_hybrid_weights')
            
            # make clusters and submit to QM clients
            system_timer('make_clusters')
            for i in frange(n_region):
                hybrid_name = 'hybrid_%d' % i
                hybrid_mark_name = 'hybrid_mark_%d' % i
                if self.get('calc_weights'):
                    hybrid = getattr(at, hybrid_name)
                    if not hasattr(at, hybrid_mark_name):
                        at.add_property(hybrid_mark_name, HYBRID_NO_MARK)
                    hybrid_mark = getattr(at, hybrid_mark_name)

                    # set marks to allow previously active atoms to become buffer atoms
                    # create_hybrid_weights will then set the buffer marks
                    hybrid_mark[hybrid_mark == HYBRID_ACTIVE_MARK] = HYBRID_BUFFER_MARK
                    hybrid_mark[hybrid == HYBRID_ACTIVE_MARK] = HYBRID_ACTIVE_MARK

                    print ('region %d, sum(hybrid) %d, sum(hybrid_mark) %d' % 
                            (i, sum(hybrid), sum(hybrid_mark)))

                    create_hybrid_weights_args = self.cluster_args.copy()
                    create_hybrid_weights_args['run_suffix'] = '_%d' % i
                    create_hybrid_weights_args_str = quippy.util.args_str(create_hybrid_weights_args)
                    print 'calling create_hybrid_weights with args_str %s' % create_hybrid_weights_args_str
                    create_hybrid_weights(at, args_str=create_hybrid_weights_args_str)

                cluster_args_str = quippy.util.args_str(self.cluster_args)
                print 'calling create_cluster_simple with args_str %s' % cluster_args_str
                c = create_cluster_simple(at, mark_name=hybrid_mark_name, args_str=cluster_args_str)
                
                client_id = i
                if self.mm_local:
                    client_id -= 1
                if self.save_clusters:
                    c.write(os.path.join(self.rundir,
                                         'cluster.client-%03d.label-%04d.xyz' %
                                         (client_id, self.label)))
                if self.test_mode:
                    clusters.append(c)
                else:
                    self.server.put(c, client_id, self.label, force_restart=self.force_restart)
                self.label += 1
            system_timer('make_clusters')

        # wait for results to be ready
        system_timer('get_results')
        if self.test_mode:
            results = []
            for i, cluster in enumerate(clusters):
                result = cluster.copy()
                result.set_cutoff(self.qm_pot.cutoff())
                result.calc_connect()
                self.qm_pot.calc(result, force=True)
                result.params['label'] = orig_label + i
                results.append(result)
        else:
            results = self.server.get_results()
        system_timer('get_results')

        system_timer('process_results')
        if self.mm_local:
            qm_results = results
        else:
            # process MM results
            mm_results, qm_results = results[:1], results[1:]
            mm_result = mm_results[0]
            at.add_property('mm_force', mm_result.force, overwrite=True)

        if do_qm:
            # process QM results
            at.add_property('qm_force', 0., n_cols=3, overwrite=True)

            # extract forces from each cluster
            for i, r in fenumerate(qm_results):
                #print 'qm forces (orig order?):'
                #print '\n'.join(['%d: pos=%s f=%s' % (j, p, f) for j, p, f in zip(r.index, r.pos, r.force)])

                client_id = i
                if self.mm_local:
                    client_id -= 1
                if self.save_clusters:
                    r.write(os.path.join(self.rundir,
                                         'results.client-%03d.label-%04d.xyz' %
                                         (client_id, r.label)))

                mark_name = 'hybrid_mark_%d' % i
                mask = getattr(r, mark_name) == HYBRID_ACTIVE_MARK
                #print 'Cluster %d: QM forces on atoms %s' % (i, r.index[mask])
                #print r.force[:, mask].T
        # HL if set_fortran is false we need to reduce the index here because
        # the atoms object is expecting python indexing.
                at.qm_force[:, [ind-1 for ind in list(r.index[mask])]] = r.force[:, mask]

        system_timer('process_results')

        # now call the parent calc() to do the force mixing etc.
        force_mixing_args = kwargs.copy()
        force_mixing_args.update(self.cluster_args)
        force_mixing_args['calc_weights'] = False # already done this above
        #print 'calling ForceMixingPotential.calc() with args %r' % force_mixing_args
        ForceMixingPotential.calc(self, at, energy, force, virial, local_energy, 
                                  local_virial, args_str, error, **force_mixing_args)
Exemple #15
0
def orthorhombic_slab(at,
                      tol=1e-5,
                      min_nrep=1,
                      max_nrep=5,
                      graphics=False,
                      rot=None,
                      periodicity=None,
                      vacuum=None,
                      shift=None,
                      verbose=True):
    """Try to construct an orthorhombic cell equivalent to the
       primitive cell `at`, using supercells up to at most `max_nrep`
       repeats. Symmetry must be exact within a tolerance of `tol`. If
       `rot` is not None, we first transform `at` by the rotation
       matrix `rot`. The optional argument `periodicity` can be used to
       fix the periodicity one or more directions. It should be a three
       component vector with value zero in the unconstrained
       directions. The vector `vacuum` can be used to add vacuum in one
       or more directions. `shift` is a three component vector which
       can be used to shift the positions in the final cell. """
    def atoms_near_plane(at, n, d, tol=1e-5):
        """Return a list of atoms within a distance `tol` of the plane defined by np.dot(n, at.pos) == d"""
        pd = np.dot(n, at.pos) - d
        return (abs(pd) < tol).nonzero()[0]

    def sort_by_distance(at, ref_atom, dir, candidates):
        """Return a copy of `candidates` sorted by perpendicular distance from `ref_atom` in direction `dir`"""
        distances_candidates = zip(
            [at.pos[dir, i] - at.pos[dir, ref_atom] for i in candidates],
            candidates)
        distances_candidates.sort()
        return [p for (d, p) in distances_candidates]

    def orthorhombic_box(at):
        """Return a copy of `at` in an orthorhombic box surrounded by vacuum"""
        at = at.copy()
        at.map_into_cell()
        at.set_lattice(
            [[2.0 * (at.pos[1, :].max() - at.pos[1, :].min()), 0.0, 0.0],
             [0.0, 2.0 * (at.pos[2, :].max() - at.pos[2, :].min()), 0.0],
             [0.0, 0.0, 2.0 * (at.pos[3, :].max() - at.pos[3, :].min())]],
            scale_positions=False)
        at.map_into_cell()
        return at

    def discard_outliers(at, indices, dirs, keep_fraction=0.5):
        """Return a copy of `indices` with the atoms with fractional coordinates along directions in `dirs`
           outside +/-`keep_fraction`/2 excluded. Lattice used is close fitting, `at.lattice`/2."""
        g = np.linalg.inv(at.lattice / 2)
        t = np.dot(g, at.pos[:, indices])
        return indices[np.logical_and(
            t[dirs, :] >= -keep_fraction / 2.0,
            t[dirs, :] < keep_fraction / 2.0).all(axis=1)]

    def check_candidate_plane(at,
                              ref_plane,
                              cand_plane,
                              dirs,
                              verbose=False,
                              label=''):
        """Check whether in-plane displacements of atoms listed in `ref_plane` match those of `cand_plane` in directions given by `dirs`"""

        # Which pair of planes has more atoms, reference or candidate?
        if len(ref_plane) < len(cand_plane):
            smaller = ref_plane
            larger = cand_plane
        else:
            smaller = cand_plane
            larger = ref_plane

        matches = {}
        for j in smaller:
            for k in larger:
                if at.z[k] == at.z[j] and abs(at.pos[dirs, k] -
                                              at.pos[dirs, j]).max() < tol:
                    matches[j] = k
                    break

        if verbose:
            print '   ', label, len(matches), '/', len(smaller), 'matches'

        return len(matches) == len(smaller)

    if rot is not None:
        at = transform(at, rot)

    xyz = fidentity(3)
    nrep = min_nrep - 1
    max_dist = fzeros(3)

    if periodicity is not None:
        periodicity = farray(periodicity)
        periodicity = dict(
            zip((periodicity != 0).nonzero()[0],
                periodicity[periodicity != 0]))
    else:
        periodicity = {}

    if verbose:
        for (dir, p) in periodicity.iteritems():
            print 'Periodicity in direction %d fixed at %f' % (dir, p)

    if graphics:
        import atomeye
        viewer = atomeye.AtomEyeViewer()

    while sorted(periodicity.keys()) != [1, 2, 3]:
        nrep += 1
        if nrep > max_nrep:
            raise ValueError('Maximum size of supercell (%d) exceeded' %
                             max_nrep)
        if verbose:
            print '\n\nSupercell %d' % nrep
        sup = supercell(at, nrep, nrep, nrep)
        box = orthorhombic_box(sup)
        box.pos[:] = box.pos - np.tile(box.pos.mean(axis=2), [box.n, 1]).T

        for dir in set([1, 2, 3]) - set(periodicity.keys()):

            if verbose:
                print '  Direction %d' % dir

            other_dirs = list(set([1, 2, 3]) - set([dir]))

            pos_index = zip(box.pos[dir, :], frange(box.n))
            pos_index.sort()

            # Find a pair of planes
            while pos_index:
                ref_pos1, ref_atom1 = pos_index.pop(0)

                # Find atom to define second plane
                while pos_index:
                    ref_pos2, ref_atom2 = pos_index.pop(0)
                    if abs(ref_pos2 - ref_pos1) > tol: break
                else:
                    continue

                ref_plane1 = atoms_near_plane(box, xyz[:, dir],
                                              box.pos[dir, ref_atom1], tol)
                ref_plane2 = atoms_near_plane(box, xyz[:, dir],
                                              box.pos[dir, ref_atom2], tol)

                # Only keep reference atoms in the centre of the cell
                ref_plane1 = discard_outliers(box, ref_plane1, other_dirs)
                ref_plane2 = discard_outliers(box, ref_plane2, other_dirs)

                if len(ref_plane1) > 2 and len(ref_plane2) > 2:
                    # Now we've got two planes, both with more than two atoms in them
                    break
            else:
                # Used up all atoms without finding two planes
                if verbose:
                    print '    No valid reference planes found.\n'
                continue

            if verbose:
                print '    Reference plane #1 through atom %d ' % ref_atom1
                print '    Reference plane #2 through atom %d at distance %r\n' % (
                    ref_atom2, ref_pos2 - ref_pos1)

            if graphics:
                highlight = fzeros(box.n)
                highlight[ref_plane1] = 1
                highlight[ref_plane2] = 2
                box.add_property('highlight', highlight, overwrite=True)
                viewer.show(box, 'highlight')
                viewer.wait()
                raw_input('continue')

            candidates = [
                i for i in frange(box.n)
                if box.pos[dir,
                           i] > box.pos[dir, ref_atom2] + max_dist[dir] + tol
            ]
            candidates = sort_by_distance(box, ref_atom1, dir, candidates)

            while candidates:
                cand1 = candidates.pop(0)

                max_dist[dir] = box.pos[dir, cand1] - box.pos[dir, ref_atom1]

                if verbose:
                    print '    Trying plane through atom %d distance %r' % (
                        cand1, max_dist[dir])

                cand_plane1 = atoms_near_plane(box, xyz[:, dir],
                                               box.pos[dir, cand1], tol)

                for cand2 in sort_by_distance(
                        box, ref_atom1, dir,
                        set(candidates) - set(cand_plane1)):
                    if abs((box.pos[dir, cand2] - box.pos[dir, cand1]) -
                           (box.pos[dir, ref_atom2] -
                            box.pos[dir, ref_atom1])) < tol:
                        if verbose:
                            print '    Found pair to plane, passing through atom %d distance %r ' % (
                                cand2,
                                box.pos[dir, cand2] - box.pos[dir, ref_atom1])
                        break
                else:
                    if verbose:
                        print '    Cannot find second candidate plane.\n'
                    candidates = sort_by_distance(
                        box, ref_atom1, dir,
                        set(candidates) - set(cand_plane1))
                    continue

                if graphics:
                    highlight[cand_plane1] = 3
                    box.highlight[:] = highlight
                    viewer.show(box, 'highlight')
                    viewer.wait()

                cand_plane2 = atoms_near_plane(box, xyz[:, dir],
                                               box.pos[dir, cand2], tol)

                if graphics:
                    highlight[cand_plane2] = 4
                    box.highlight[:] = highlight
                    viewer.show(box, 'highlight')
                    viewer.wait()

                    highlight[cand_plane1] = 0
                    highlight[cand_plane2] = 0

                # Remove cand_plane1 from list of candidates
                candidates = sort_by_distance(
                    box, ref_atom1, dir,
                    set(candidates) - set(cand_plane1))

                # Check ref_plane1 against cand_plane1 and ref_plane2 against cand_plane2 in directions
                # listed in other_dirs
                match1 = check_candidate_plane(box, ref_plane1, cand_plane1,
                                               other_dirs, verbose,
                                               'Plane #1:')
                match2 = check_candidate_plane(box, ref_plane2, cand_plane2,
                                               other_dirs, verbose,
                                               'Plane #2:')

                if match1 and match2:
                    periodicity[dir] = box.pos[dir, cand1] - box.pos[dir,
                                                                     ref_atom1]
                    if verbose:
                        print '\n  Periodicity in direction %d is %f\n' % (
                            dir, box.pos[dir, cand1] - box.pos[dir, ref_atom1])

                    if graphics:
                        highlight[cand_plane1] = 3
                        highlight[cand_plane2] = 3
                        box.highlight[:] = highlight
                        viewer.show(box, 'highlight')
                        viewer.wait()
                        raw_input('continue...')
                    break

                if graphics:
                    raw_input('continue...')
            else:
                # Failed to find match for direction dir
                continue

    # Finally, construct new cell by selecting atoms within first unit cell
    lattice = farray(np.diag([periodicity[1], periodicity[2], periodicity[3]]))
    g = np.linalg.inv(lattice)

    nrepx, nrepy, nrepz = fit_box_in_cell(periodicity[1], periodicity[2],
                                          periodicity[3], at.lattice)

    sup = supercell(at, nrepx, nrepy, nrepz)
    sup.map_into_cell()

    # small shift to avoid coincidental cell alignments
    delta = np.tile([0.01, 0.02, 0.03], [sup.n, 1]).T
    if shift is not None and vacuum is not None:
        delta = delta + np.tile(shift, [sup.n, 1]).T
    t = np.dot(g, sup.pos) + delta

    orthorhombic = sup.select(np.logical_and(t >= -0.5, t < 0.5).all(axis=1))

    if vacuum:
        lattice = farray(np.diag(lattice.diagonal() + vacuum))

    if shift is not None and vacuum is None:
        if verbose:
            print 'Shifting positions by %s' % np.dot(lattice, shift)
        orthorhombic.pos += np.tile(np.dot(lattice, shift),
                                    [orthorhombic.n, 1]).T

    orthorhombic.set_lattice(lattice, scale_positions=False)
    orthorhombic.map_into_cell()
    return orthorhombic
Exemple #16
0
    def write(self, at):
        if "dan_graph" in at.params:
            if type(at.params["dan_graph"]) is StringType:
                graph_vals = at.params["dan_graph"].split()
            else:
                graph_vals = [at.params["dan_graph"]]
            self.n_graphs = len(graph_vals)

        if self.first_config:
            if self.n_graphs > 0:
                self.out.write("n_graphs %d\n" % self.n_graphs)
                self.graph_min = [1.0e38] * self.n_graphs
                self.graph_max = [-1.0e38] * self.n_graphs
            self.first_config = False
        self.out.write("new_configuration" + "\n")

        self.out.write("pbc_a 1 %f %f %f\n" %
                       (at.lattice[1, 1], at.lattice[2, 1], at.lattice[3, 1]))
        self.out.write("pbc_a 2 %f %f %f\n" %
                       (at.lattice[1, 2], at.lattice[2, 2], at.lattice[3, 2]))
        self.out.write("pbc_a 3 %f %f %f\n" %
                       (at.lattice[1, 3], at.lattice[2, 3], at.lattice[3, 3]))

        for i_at in frange(at.n):
            if self.atom_type is not None:
                atom_type = self.atom_type
            else:
                if (hasattr(at, 'z')):
                    atom_type = 'z'
                else:
                    if (hasattr(at, 'species')):
                        atom_type = 'species'
                    else:
                        if (hasattr(at, 'type')):
                            atom_type = 'type'
                        else:
                            raise ValueError(
                                "Can't find z, species, or type for atom type")

            px = getattr(at, self.pos_field)[1, i_at]
            py = getattr(at, self.pos_field)[2, i_at]
            pz = getattr(at, self.pos_field)[3, i_at]
            try:
                self.out.write(
                    "atom %f %f %f %s" %
                    (px, py, pz, getattr(at, atom_type)[i_at].stripstrings()))
            except:
                self.out.write("atom %f %f %f %s" %
                               (px, py, pz, getattr(at, atom_type)[i_at]))

            if self.value is not None:
                if (type(self.value) is StringType):
                    self.value = [self.value]
                for iv in range(len(self.value)):
                    self.out.write(" value %d %s" %
                                   (iv + 1, getattr(at, self.value[iv])[i_at]))
            self.out.write("\n")

            if self.vector is not None:
                if hasattr(at, self.vector):
                    self.out.write(
                        "vector %f %f %f   %f %f %f\n" %
                        (px, py, pz, getattr(at, self.vector)[1, i_at],
                         getattr(at, self.vector)[2, i_at],
                         getattr(at, self.vector)[3, i_at]))

        if self.n_graphs > 0:
            for ig in range(len(graph_vals)):
                val = graph_vals[ig]
                self.out.write("graph_value %d %f\n" % (ig, float(val)))
                f_val = float(val)
                if (f_val < self.graph_min[ig]):
                    self.graph_min[ig] = f_val
                if (f_val > self.graph_max[ig]):
                    self.graph_max[ig] = f_val

        if self.bond_by_cutoff:
            self.out.write("bond_by_cutoff\n")

        if self.post_config_command is not None:
            if (type(self.post_config_command) is StringType):
                self.post_config_command = [self.post_config_command]
            for cmd in self.post_config_command:
                self.out.write(cmd + "\n")