Source code for muesr.utilities.dft_grid
from muesr.core.parsers import *
from muesr.core.cells import get_reduced_bases
from muesr.settings import config
import numpy as np
#@profile
[docs]def build_uniform_grid(sample, size, min_distance_from_atoms=1.0):
"""
Generates a grid of symmetry inequivalent interstitial
positions with a specified minimum distance from the atoms of the
sample. Especially intended for DFT simulations.
:param sample: A sample object.
:param int size: The number of steps in the three lattice directions.
Only equispaced grids are supported at the moment.
:param float min_distance_from_atoms: Minimum distance between a
interstitial position and the
atoms of the lattice.
Units are Angstrom.
:returns: A list of symmetry inequivalent positions.
:rtype: list
"""
tolerance = config.FCRD
#build uniform grid
npoints = size**3
x_ = np.linspace(0., 1., size, endpoint=False)
y_ = np.linspace(0., 1., size, endpoint=False)
z_ = np.linspace(0., 1., size, endpoint=False)
uniform_grid = np.meshgrid(x_, y_, z_, indexing='ij')
x,y,z = uniform_grid
equiv=np.ones_like(x)*(npoints)
alt = True
nb_cells = np.array([[-1., -1., -1.],
[-1., -1., 0.],
[-1., -1., 1.],
[-1., 0., -1.],
[-1., 0., 0.],
[-1., 0., 1.],
[-1., 1., -1.],
[-1., 1., 0.],
[-1., 1., 1.],
[ 0., -1., -1.],
[ 0., -1., 0.],
[ 0., -1., 1.],
[ 0., 0., -1.],
[ 0., 0., 0.],
[ 0., 0., 1.],
[ 0., 1., -1.],
[ 0., 1., 0.],
[ 0., 1., 1.],
[ 1., -1., -1.],
[ 1., -1., 0.],
[ 1., -1., 1.],
[ 1., 0., -1.],
[ 1., 0., 0.],
[ 1., 0., 1.],
[ 1., 1., -1.],
[ 1., 1., 0.],
[ 1., 1., 1.]])
for i in range(size):
for j in range(size):
for k in range(size):
if equiv[i,j,k] < npoints:
#this point is equivalent to someone else!
continue
for r,t in sample.sym.get_symop():
# new position for the muon
n = np.zeros(3)
# apply symmetry and bring back to unit cell
n = np.round(np.dot(r,[x[i,j,k],y[i,j,k],z[i,j,k]])+t,decimals=config.FCRD)%1
if (np.abs(n*size - np.rint(n*size)) < 10**-(config.FCRD)).all():
#get index of point
ii,jj,kk=np.rint(n*size).astype(int)
if (ii*(size**2)+jj*size+kk > i*(size**2)+j*size+k):
equiv[ii,jj,kk] -= 1
equiv[i,j,k] += 1
reduced_bases = sample.cell.get_cell()
scaled_pos = sample.cell.get_scaled_positions()
positions = []
for i in range(size):
for j in range(size):
for k in range(size):
if equiv[i,j,k] >= npoints:
#saves distances with all atoms
distances = []
dists = np.zeros(27*len(sample._cell))
center = [x[i,j,k],y[i,j,k],z[i,j,k]]
#check distances form atoms (also in neighbouring cells)
for a in range(len(sample._cell)):
dists[a*27:(a+1)*27] = np.linalg.norm(
np.dot(scaled_pos[a] - center + nb_cells,
reduced_bases), axis=1 )
#
# old method 20 times slower!
#
#for a in range(len(sample._cell)):
# for ii in (-1, 0, 1):
# for jj in (-1, 0, 1):
# for kk in (-1, 0, 1):
# distances.append( np.linalg.norm(
# np.dot(scaled_pos[a] - center + np.array([ii,jj,kk]),
# reduced_bases) ) )
#
#print(np.allclose(dists,distances))
#if min(distances) > min_distance_from_atoms:
if dists.min() > min_distance_from_atoms:
positions.append([x[i,j,k],y[i,j,k],z[i,j,k]])
return positions
if __name__ == "__main__":
unittest.main()