from __future__ import division, unicode_literals, absolute_import
import ctypes
from tabulate import tabulate
#from ctypes import *
from ctypes import cdll
from ctypes import c_float, byref
import numpy as np
import traceback, os, sys, datetime, glob, copy
from siman import header
from siman.header import print_and_log, printlog, geo_folder, runBash
# from siman.classes import CalculationVasp, Structure
from siman.core.structure import Structure
from siman.set_functions import InputSet
from siman.functions import return_atoms_to_cell, element_name_inv
from siman.inout import write_xyz
from siman.geo import local_surrounding, local_surrounding2, xcart2xred, xred2xcart
from siman.small_functions import block_print, enable_print
libfile = os.path.dirname(__file__)+'/libfindpores.so'
if os.path.exists(libfile):
lib = cdll.LoadLibrary(libfile)
else:
lib = None
printlog('Warning! libfindpores.so is not available, search of voids is not possible. Please compile it inside siman folder using makefile')
[docs]def create_c_array(pylist, ctype):
if ctype == float:
c_array = (ctypes.c_float * len(pylist))(*pylist)
return c_array
[docs]def find_pores(st_in, r_matrix=1.4, r_impurity = 0.6, step_dec = 0.05, fine = 0.3, prec = 0.1, calctype = 'central', gbpos = 0,
find_close_to = (), check_pore_vol = 0):
"""
st_in - input Structure() object
r_impurity (A)- all pores smaller than this radius will be found
r_matrix (A) - radius of matrix atoms disregarding to their type
step_dec - scanning step of the cell in Angstroms
fine - allows to change density of local points; local_step = step_dec/fine
prec - precicion of pore center determination
check_pore_vol - allows to estimate volume of pores; has problems for big cells
'find_close_to' - works in the cases of gb and grain_vol; allows to ignore all and find pore close to provided three reduced coordinates
return - instance of Structure() class with coordinates of pores. Number and type of included pores depend on the argument of 'calctype'.
"""
xred = st_in.xred
natom = len(xred)
rprimd = st_in.rprimd
name = st_in.name
#print xred
"""Additional values"""
# check_pore_vol = 1
#if calctype in ("pore_vol","gb","grain_vol","all_local" ): check_pore_vol = 1 #something wrong with this function, especially for big cells
"""----Conversions of types for C++"""
r1 = create_c_array(rprimd[0], float)
r2 = create_c_array(rprimd[1], float)
r3 = create_c_array(rprimd[2], float)
xred1 = (c_float * len(xred))(*[x[0] for x in xred])
xred2 = (c_float * len(xred))(*[x[1] for x in xred])
xred3 = (c_float * len(xred))(*[x[2] for x in xred])
max_npores = 10000;
ntot = ctypes.c_int32(); npores = ctypes.c_int32()
l_pxred1 = (c_float * max_npores)(0) #make static arrays fol local points
l_pxred2 = (c_float * max_npores)(0)
l_pxred3 = (c_float * max_npores)(0)
l_npores = (ctypes.c_int32 * max_npores)(0)
pxred1 = (c_float * max_npores)(0) #make static arrays natoms + npore
pxred2 = (c_float * max_npores)(0)
pxred3 = (c_float * max_npores)(0)
"""----Run C++ function"""
print_and_log("Starting C++ function lib.findpores()...\n")
# print(r_matrix, r_impurity, step_dec, fine, prec)
lib.findpores ( check_pore_vol, \
max_npores, \
byref(ntot), l_pxred1, l_pxred2, l_pxred3, l_npores, \
byref(npores), pxred1, pxred2, pxred3, \
natom, xred1, xred2, xred3, \
c_float(r_matrix), c_float(r_impurity), c_float(step_dec), c_float(fine), c_float(prec), \
r1, r2, r3 )
print_and_log( "ntot is ", ntot.value)
print_and_log( "l_npores[0] is ",l_npores[0])
v = np.zeros((3))
l_pxred = []
shift1 = 0; shift2 = 0
for i_por in range(npores.value):
l_pxred.append( [] )
shift2+=l_npores[i_por]
for i in range(shift1, shift2):
v[0] = l_pxred1[i]; v[1] = l_pxred2[i]; v[2] = l_pxred3[i]
l_pxred[i_por].append( v.copy() )
shift1 = shift2
if shift2 != ntot.value:
print_and_log( "Error! final shift2 not equal to ntot")
#print l_pxred[0]
pxred = [] # only coordinates of pores
#print pxred1[natom]
for i in range(npores.value):
v[0] = pxred1[i+natom]; v[1]= pxred2[i+natom]; v[2] = pxred3[i+natom] #with shift, because first natom elements are coordinates of atoms
pxred.append( v.copy() )
#print pxred
"""----End of C++; result is two lists: lpxred - local geometry of all pores, pxred - coordinates of all pores"""
""" Analyse of pores """
# st_result = Structure()
st_result = st_in.new()
st_result.rprimd = rprimd
targetp = np.array((0.,0.,0.))
if find_close_to:
targetp = np.asarray(find_close_to) #targer point
print_and_log( "Target point is ",targetp)
a = step_dec/fine #the side of little cube formed by the mesh which is used to find spheres inside the pore.
aaa = a*a*a
#find most central pore
if calctype == 'central': #return coordinates of the most central pore
st_result.name = "central_pore_from "+name
center = np.array((0.5,0.5,0.5))#center of cell
d_min = 100
for x in pxred:
d = np.linalg.norm(x - center)
#print x, x-center, d
if d < d_min and x[0] <= 0.5 and x[1] <= 0.5 and x[2] <= 0.5:
d_min = d
x_min = x
print_and_log( "The closest pore to the center has coordinates",x_min)
st_result.xred.append( x_min )
elif calctype == 'gb': #add impurity at gb
st_result.name = "gb_pore_from "+name
d_min = 100; #d2_min = 100
dt_min =100
i_min = 0; x_min = np.zeros((3))
for i, x in enumerate(pxred):
#print "l_npores ",l_npores[i]
d = abs(x[0] - gbpos/rprimd[0][0]) #
#print x[0], d
if find_close_to: closer = (np.linalg.norm(targetp - x) < dt_min)
else: closer = ( d < d_min ) # and x[1]>0.3 and x[2]>0.3:
if closer:
x_pre = x_min
i_pre = i_min
d_min = d
dt_min = np.linalg.norm(targetp - x)
x_min = x
i_min = i
#find and add impurity in bulk
#d2 = abs( x[0] - (gbpos/rprimd[0][0] - 0.25) )
#if d2 < d2_min:
# d2_min = d2
# x2_min = x
# i2_min = i
#print "rprimd[0][0]", rprimd[0][0]
print_and_log( "Position of boundary is ",gbpos/rprimd[0][0])
#x_min[0] = gbpos/rprimd[0][0]
if find_close_to: print_and_log( "The closest pore to the target point is [ %.2f %.2f %.2f ]"%(x_min[0], x_min[1], x_min[2]))
else: print_and_log( "The closest pore to the gb has coordinates",x_min)
st_result.xred.append( x_min )
#st_result.xred.append( x_pre )
#Calculate volume of the pore using local balls:
print_and_log( "The number of pore is ",i_min," ; It has ",l_npores[i_min], "local balls")
print_and_log( "Volume of pore is ", l_npores[i_min] * a*a*a, " A^3")
#st_result.xred.extend( l_pxred[i_min] )
#st_result.xred.extend( l_pxred[i_pre] )
#print "The closest pore to the center of bulk has coordinates",x2_min
#st_result.xred.append( x2_min )
#Calculate volume of the pore using local balls:
#print "The number of bulk pore is ",i2_min," ; It has ",l_npores[i2_min], "local balls"
#print "Volume of pore is ", l_npores[i2_min] * a*a*a, " A^3";
#st_result.xred.extend( l_pxred[i2_min] )
elif calctype == 'grain_vol': #add impurity to volume of grain
st_result.name = "grain_volume_pore_from "+name
d2_min = 100
dt_min = 100
i_min = 0; x_min = np.zeros((3))
for i, x in enumerate(pxred):
#find and add impurity to the bulk
d2 = abs( x[0] - (gbpos/rprimd[0][0] - 0.25) )
if find_close_to: closer = (np.linalg.norm(targetp - x) < dt_min)
else: closer = ( d2 < d2_min ) # and x[1]>0.3 and x[2]>0.3:
if closer:
dt_min = np.linalg.norm(targetp - x)
d2_min = d2
x2_min = x
i2_min = i
if find_close_to: print_and_log( "The closest pore to the target point is [ %.2f %.2f %.2f ]"%(x2_min[0], x2_min[1], x2_min[2]))
else: print_and_log( "The closest pore to the center of bulk has coordinates",x2_min)
st_result.xred.append( x2_min )
#Calculate volume of the pore using local balls:
print_and_log( "The number of bulk pore is ",i2_min," ; It has ",l_npores[i2_min], "local balls")
print_and_log( "Volume of pore is ", l_npores[i2_min] * a*a*a, " A^3")
st_result.xred.extend( l_pxred[i2_min] )
elif calctype == 'all_local':
st_result.name = "all_local_points_from "+name
v_max = 0
i_max = 0
for i in range(npores.value):
v_pore = l_npores[i] * aaa
print_and_log( "Volume of pore is ", l_npores[i] * aaa, " A^3")
if v_pore > v_max: v_max = v_pore; i_max = i
print_and_log( "Pore number ", i_max,"has the largest volume ", v_max," A^3")
# st_result.xred = l_pxred[i_max] # here coordinates of all local points to show geometry of pore with largerst volume
st_result.xred = [x for group in l_pxred for x in group ] # all pores
elif calctype == 'all_pores':
st_result.name = "all_local_pores_from "+name
st_result.xred = pxred
st_result.rprimd = rprimd
st_result.xred2xcart()
st_result.typat = [1 for x in st_result.xred]
st_result.ntypat = 1
st_result.natom = len(st_result.typat)
st_result.znucl = [200]
st_ntypat = 1
return st_result
[docs]def add_impurity(it_new, impurity_type = None, addtype = 'central', calc = [], r_pore = 0.5,
it_to = '', ise_to = '', verlist_to = [], copy_geo_from = "", find_close_to = (),add_to_version = 0,
write_geo = True, only_version = None, fine = 4, put_exactly_to = None, check_pore_vol = 0, replace_atom = None, override = False):
"""
Add impurities in pores.
Input:
it_new - name of new structure with impurity
impurity_type - name of impurity from Mendeley table, for example 'C'
addtype - type of adding: ['central',]; 'central' means that impurity
will be placed as close to the geometrical center of cell as possible.
it_to , ise_to , verlist_to - completed calculations in which impurity
will be added
if 'verlist_to' is empty, function will try to find geometry files in 'geo_folder + struct_des[it_to].sfolder' folder;
even if 'it_to' is empty it will try to find files in 'geo_folder + struct_des[it_new].sfolder+'/from' ' folder.
'ise_to' also can be empty
if 'copy_geo_from' is not empty, then programm copy all files from folder 'copy_geo_from' to
folder 'geo_folder + struct_des[it_to].sfolder+"/"+it_to' or 'geo_folder + struct_des[it_new].sfolder+"/from" '
'find_close_to' is tuple of three reduced coordinates of point close to which you want to find impurity. If empty - ignored;
'add_to_version' is integer number added to each 'verlist_to' number to produce ver_new.
'only_version' - if == [v,], then instertion will be provided only for v. If None insertion will be made in all found versions
If you want to add impurity to relaxed structure ...
'fine' - integer number; allows to reduce number of small steps for defining center
Possible addtype's:
'central' - add one atom to the pore which is most close to the center of the cell but with reduced coordinates less than 0.5 0.5 0.5
'all_pore' - add atoms in every found pore
'all_local' - add atoms to every local point which allows to visualise topology of pores.
'gb' - uses self.gbpos and places atom close to this value assuming that it will be at gb
'grain_vol' - uses self.gbpos and assuming that cell contains two gb and two equal grains, places atom close to the centre of grain; y and z can be arbiratry
put_exactly_to - will add impurity to this point
find_close_to - will try to find closest void and insert pore here.
check_pore_vol - allows to estimate volume of pores; has problems for big cells
replace_atom - if not None, than the specified atom is substituted
Side effects: creates new geometry folder with input structures;
"""
struct_des = header.struct_des
def test_adding_of_impurities(added, init, v):
"""
Can be used only inside add_impurity()
Replicates the structure and find again pores
"""
global natoms_v1
if added == None: return
if v == 1: #TEST
natoms_v1 = len(added.init.xcart) # for test
st_rep_after = added.init.replic( (1,2,1) )
rep = copy.deepcopy(init)
rep.init = rep.init.replic( (1,2,1) );
#print rep
rep = add(znucl, "", rep, write_geo = False)
#print rep
#print "xcart of replic after adding ", st_rep_after.xcart
#print "xcart of adding to replic ", rep.init.xcart
if len(st_rep_after.xcart) != len(rep.init.xcart): raise RuntimeError
p = 0
#for x2 in st_rep_after.xcart:
# print x2
for x in rep.init.xcart:
a = any( ( np.around(x2, p) == np.around(x, p) ).all() for x2 in st_rep_after.xcart )
#b = any( ( np.ceil(x2, p) == np.ceil(x, p) ).all() for x2 in st_rep_after.xcart )
#c = any( ( np.floor(x2, p) == np.floor(x, p) ).all() for x2 in st_rep_after.xcart )
#print a, b, c
#np.concatenate(a, b, c):
if not a:
print_and_log( "Error! Can't find ", np.around(x,3), "in replic ")
raise RuntimeError
#assert all([ all( np.around(v1, 8) == np.around(v2, 8) ) for (v1, v2) in zip(st_rep_after.xcart, rep.init.xcart) ])
print_and_log( "add_impurity: test succesfully done")
if natoms_v1 != len(added.init.xcart): print_and_log("You have different number of pores in different versions\n"); raise RuntimeError
return
def add(znucl, xyzpath = "", new = None, write_geo = True, put_exactly_to = None):
"if put_exactly_to is True, then atom just added and nothing are searched"
if write_geo and os.path.exists(new.path["input_geo"]) and not override:
print_and_log("add: File '"+new.path["input_geo"]+"' already exists; continue\n", imp = 'Y');
return new
#new.init = return_atoms_to_cell(new.init)
if replace_atom:
#atom substitution
if znucl not in new.init.znucl:
new.init.znucl.append(znucl)
new.init.ntypat+=1
new.init.typat[replace_atom] = new.init.ntypat
else:
ind = new.init.znucl.index(znucl)
new.init.typat[replace_atom] = ind + 1
new.init.nznucl = []
for typ in range(1, new.init.ntypat+1):
new.init.nznucl.append(new.init.typat.count(typ) )
else:
new_before = copy.deepcopy(new)
# new.init.xcart[-2][0]-=0.9 #was made once manually for c1gCOi10.1
# new.init.xcart[-2][2]+=0.2
# new.init.xred = xcart2xred(new.init.xcart, new.init.rprimd)
write_xyz(new.init)
#step = 0.042
step = 0.06
#r_pore = 0.56
#fine = 0.3 # for visualisation of pores
#fine = 4 #controls small steps; the steps are smaller for larger numbers
#r_pore = 0.54
prec = 0.004 # precision of center Angs
if new.hex_a == None:
r_mat = 1.48 -step
else:
r_mat = new.hex_a / 2 - step
if put_exactly_to:
pores_xred = [np.array(put_exactly_to),]
print_and_log( 'Inmpurity just put in ', pores_xred, imp = 'Y')
else:
pores = find_pores(new.init, r_mat, r_pore, step, fine, prec, addtype, new.gbpos, find_close_to, check_pore_vol) #octahedral
pores_xred = pores.xred
npores = len(pores_xred)
st = new.init
#delete last oxygen; was made once manually for c1gCOi10.1
# st.natom-=1
# del st.xred[-1]
# del st.typat[-1]
st.natom += npores
st.xred.extend( pores_xred )
if znucl in st.znucl:
print_and_log( "znucl of added impurity is already in cell")
ind = st.znucl.index(znucl)
typat = ind+1
st.nznucl[ind]+=npores
else:
st.ntypat +=1
typat = st.ntypat
st.znucl.append( znucl )
st.nznucl.append( npores )
for i in range( npores ):
st.typat.append( typat )
st.xred2xcart()
new.init = st
#print "Add impurity: len(xred ", len(new.init.xred)
#print "natom", new.init.natom
#For automatisation of fit
try:
#new.build
if new.build.nadded == None: new.build.nadded=npores
else: new.build.nadded+=npores
if new.build.listadded == [None]: new.build.listadded = range(new.natom - npores, new.natom) #list of atoms which were added
else: new.build.listadded.extend( range(new.natom - npores, new.natom) )
#print "Warning!!! Information about added impurities rewritten"
except AttributeError:
pass
#new.init.znucl = new.znucl
#new.init.typat = new.typat
#write_xyz(replic(new.init, (2,1,2)) , xyzpath)
#test_adding_of_impurities(new, new_before, v)
print_and_log("Impurity with Z="+str(znucl)+" has been added to the found pore in "+new.name+"\n\n")
if write_geo:
write_xyz(new.init , xyzpath)
new.write_geometry("init",new.des, override = override)
print_and_log( "\n")
return new
"""0.Begin----------------------------------------------------------------------------"""
znucl = element_name_inv(impurity_type)
if impurity_type != 'octa' and impurity_type not in it_new:
print_and_log("add_impurity: Your name 'it_new' is incorrect!\n\n")
raise RuntimeError
#del header.history[-2]
#
#hstring = ("add_impurity('%s', '%s', '%s', calc, %.3f, '%s', '%s', %s, '%s') #at %s" %
# (it_new, impurity_type, addtype, r_pore,
# it_to, ise_to, verlist_to, copy_geo_from,
# datetime.date.today() ) )
hstring = ("%s #on %s"% (traceback.extract_stack(None, 2)[0][3], datetime.date.today() ) )
if hstring != header.history[-1]: header.history.append( hstring )
#geo_exists =
"""1. The case of insertion to existing calculations--------------------------------------------------"""
if verlist_to:
for v in verlist_to:
if only_version and v not in only_version: continue # only_version = None works for all versions
id = (it_to, ise_to, v)
new = copy.deepcopy(calc[id])
new.init = new.end #replace init structure by the end structure
new.version = v+add_to_version
new.name = it_new#+'.'+id[1]+'.'+str(id[2])
new.des = 'Obtained from '+str(id)+' by adding '+impurity_type+' impurity '
path_new_geo = struct_des[it_new].sfolder+"/"+it_new+"/"+it_new+'.imp.'+addtype+'.'+str(new.version)+'.'+'geo'
new.init.name = it_new+".init."+str(new.version)
xyzpath = struct_des[it_new].sfolder+"/"+it_new
new.path["input_geo"] = geo_folder+path_new_geo
print_and_log("File '"+new.path["input_geo"] +"' with impurity will be created\n");
#new.init.name = 'test_before_add_impurity'
new = add(znucl, xyzpath, new, write_geo, put_exactly_to = put_exactly_to)
"""2. The case of insertion to geo files------------------------------------------------------------"""
else:
""" Please rewrite using new functions """
print_and_log("You does not set 'id' of relaxed calculation. I try to find geometry files in "+it_new+" folder\n")
if it_to: geo_path = geo_folder + struct_des[it_to].sfolder + "/" + it_to
else: geo_path = geo_folder + struct_des[it_new].sfolder + "/" + it_new+'/from'
if copy_geo_from:
print_and_log("You asked to copy geo files from "+copy_geo_from+" to " +geo_path+ " folder\n")
#if not os.path.exists(os.path.dirname(geo_path)):
runBash( "mkdir -p "+geo_path )
runBash( "cp "+copy_geo_from+"/* "+geo_path )
if os.path.exists(geo_path):
print_and_log("Folder '"+geo_path +"' was found. Trying to add impurity\n");
else:
print_and_log("Error! Folder "+geo_path+" does not exist\n"); raise RuntimeError
#geofilelist = glob.glob(geo_path+'/*.geo*') #Find input_geofile
#geofilelist = runBash('find '+geo_path+' -name "*grainA*.geo*" ').splitlines()
#geofilelist = runBash('find '+geo_path+' -name "*.geo*" ').splitlines()
geofilelist = glob.glob(geo_path+'/*.geo*')
print_and_log( "There are several files here already: ", geofilelist, imp = 'y' )
#print 'find '+geo_path+' -name "*.geo*" ',geofilelist
#return
for input_geofile in geofilelist:
v = int( runBash("grep version "+str(input_geofile) ).split()[1] )
if only_version and v not in only_version: continue # only_version = None works for all versions
new = CalculationVasp()
new.version = v
new.name = input_geofile
new.read_geometry(input_geofile)
init = copy.deepcopy(new)
igl = input_geofile.split("/")
#new.name = igl[-3]+'/'+igl[-3] #+input_geofile
new.name = struct_des[it_new].sfolder+"/"+it_new+"/"+it_new
print_and_log( "New path and part of name of file is ", new.name, imp = 'Y')
#return
new.des = 'Obtained from '+input_geofile+' by adding '+impurity_type+' impurity '
#new.init.xred = new.xred
#new.init.rprimd = new.rprimd
#print new.rprimd
new.init.name = new.name+'.imp.'+addtype+'.'+str(new.version)
#new.path["input_geo"] = geo_folder+it_new+"/"+new.end.name+'.'+'geo'
new.path["input_geo"] = geo_folder+"/"+new.init.name+'.'+'geo'
#new.init.name = 'test_before_add_impurity'
new = add(znucl, "", new, write_geo, put_exactly_to = put_exactly_to)
return new.path["input_geo"] #return for last version
[docs]def insert_cluster(insertion, i_center, matrix, m_center):
"""
Take care of orientation; typat should be consistent
Input:
insertion - object of class Structure(), which is supposed to be inserted in matrix
in such a way that i_center will be combined with m_center.
matrix - object of class Structure().
i_center, m_center - numpy arrays (3) cartesian coordinates
"""
ins = copy.deepcopy(insertion)
mat = copy.deepcopy(matrix)
r = mat.rprimd
hproj = [ (r[0][i]+r[1][i]+r[2][i]) * 0.5 for i in (0,1,2) ] #projection of vectors on three axis
if 1:
for i, x in enumerate(ins.xcart):
ins.xcart[i] = x - i_center
for i, x in enumerate(mat.xcart):
mat.xcart[i] = x - m_center
max_dis = 1
for i_x, ix in enumerate(ins.xcart):
dv_min = max_dis
print_and_log( "Insertion atom ",ix,)
if 1:
for j, mx in enumerate(mat.xcart):
dv = mx - ix
for i in 0,1,2:
if dv[i] > hproj[i]: dv = dv - mat.rprimd[i] #periodic boundary conditions - can be not correct (in the sense that closest image can lie not 100 % in the neighbourhood image cell ) for oblique cells and large absolute values of dv
if dv[i] < -hproj[i]: dv = dv + mat.rprimd[i]
len1 = np.linalg.norm(dv)
len2, second_len2 = mat.image_distance(mx, ix, r, 1) #check len1
#print "Lengths calculated with two methods ", len1, len2
len1 = len2 #just use second method
#assert np.around(len1,1) == np.around(len2,1)
if len1 < dv_min:
dv_min = len1;
j_r = j # number of matrix atom to replace
if 1:
#Modify to replace overlapping atoms
if dv_min == max_dis:
print_and_log( " is more far away from any matrix atom than ",dv_min," A; I insert it")
# mat.xcart.append( ix )
# print_and_log( 'type of added atom is ', ins.typat[i_x])
# mat.typat.append( ins.typat[i_x] )
mat = mat.add_atom(xc = ix, element = ins.get_elements()[i_x] )
else:
print_and_log( "will replace martix atom", mat.xcart[j_r] )
mat.xcart[j_r] = ix.copy()
mat.rprimd = r
mat.xcart2xred()
mat.natom = len(mat.xcart)
mat.name = 'test_of_insert'
st = mat
# print(st.natom, len(st.xcart), len(st.typat), len(st.znucl), max(st.typat) )
# write_xyz(mat)
mat = mat.return_atoms_to_cell()
mat.write_poscar()
return mat
#write_xyz(mat)
[docs]def make_interface(main_slab, m_xc, second_slab, s_xc):
"""
Make interfaces
Both slabs should have close sizes along x and y and should be oriented correctly
Input:
main_slab (Structure) - slab
second_slab (Structure) - slab, scaled to coincide with the main slab
m_xc, s_xc (array(3)) - cartesian coordinates of pointis in main_slab and secondary slab to be combined
Return Slab with interface and scaled second slab
"""
ins = copy.deepcopy(second_slab)
mat = copy.deepcopy(main_slab)
if 1:
#scale insertion
mr = mat.rprimd_len()
ir = ins.rprimd_len()
print('Matrix vlength', mr)
print('Insert vlength', ir)
x_scale = mr[0]/ ir[0]
y_scale = mr[1]/ ir[1]
print('Scaling factors', x_scale, y_scale)
# print('i_center', i_center)
ins.rprimd[0] = ins.rprimd[0]*x_scale
ins.rprimd[1] = ins.rprimd[1]*y_scale
ir = ins.rprimd_len()
s_xred = xcart2xred([s_xc], ins.rprimd)[0]
print('Insert vlength after scaling', ir)
ins.update_xcart()
# ins.xcart2xred()
ins_sc = ins.copy()
ins_sc.name+='_scaled'
s_xc = xred2xcart([s_xred], ins.rprimd)[0]
# print('i_center', i_center)
if 1:
for i, x in enumerate(ins.xcart):
ins.xcart[i] = x - s_xc
for i, x in enumerate(mat.xcart):
mat.xcart[i] = x - m_xc
for i_x, ix in enumerate(ins.xcart):
mat = mat.add_atom(xc = ix, element = ins.get_elements()[i_x] )
mat.xcart2xred()
mat.natom = len(mat.xcart)
mat.name += 'inteface'
mat = mat.return_atoms_to_cell()
mat = mat.shift_atoms([0,0,0.5])
return mat, ins_sc
[docs]def make_interface2(st1, st2, shift, mesh = [5,5], oxi = {}, at_fixed = [], mode = "top", tol = 0.05):
"""
Creates interface from two input structures
INPUT:
st1 (Structure) - main input structure
st2 (Structure) - appending input structure
thickness (float) - remaining thickness of vacuum
shift (float array) - shift atoms along axis
tol (float) - relative difference between two steps with different meshes
mode (str) - type of interfaces or interfaces that will be returned
'top' - returns one structure that was made from highest atoms from st1 and lowest atom from st2
'min' - returns
'min' mode requires these arguments:
mesh (int array) - mesh in AB plane to find a structure with lowest Ewald energy
oxi (float dic) - dictionary oxidation states. Look siman.geo.make_neutral() for more details
E.g {"Li": 1, "La": 2, "Zr":4, "O": -1}
at_fixed (string array) - atoms with fixed oxidation states
RETURN:
interface and new structure st2.
author - A. Burov
"""
from pymatgen.analysis.ewald import EwaldSummation
st1 = copy.deepcopy(st1)
st2 = copy.deepcopy(st2)
st1_tmp = copy.deepcopy(st1)
st2_tmp = copy.deepcopy(st2)
st1_tmp = st1_tmp.remove_vacuum()
st2_tmp = st2_tmp.remove_vacuum()
z1 = st1_tmp.rprimd[2][2]
z2 = st2_tmp.rprimd[2][2]
st2 = st2.add_vacuum(vector_i = 2, thickness = z1 - z2)
z_coord1 = [x[-1] for x in st1.xcart]
z_coord2 = [x[-1] for x in st2.xcart]
z1_max = [x for _, x in sorted(zip(z_coord1, range(st1.natom)))][-1]
z2_min = [x for _, x in sorted(zip(z_coord2, range(st2.natom)))][0]
block_print()
interface, li_super_new = make_interface(st1, st1.xcart[z1_max], st2, st2.xcart[z2_min]+shift)
interface = interface.remove_vacuum(thickness = abs(shift[-1]))
z3 = interface.rprimd[2][2]
enable_print()
structures = []
if (mode == "min"):
print("Finding structure with minimal Ewald energy")
shift_x = interface.rprimd[0][0] / mesh[0]
shift_y = interface.rprimd[1][1] / mesh[1]
en_min = 1e20
diff = 1.0
itr = 0
while (diff > tol):
en_prev = en_min
en_min = 1e20
itr += 1
for x in range(mesh[0]):
for y in range(mesh[1]):
block_print()
interface_c = copy.deepcopy(interface)
for xc in interface_c.xcart:
if (xc[2] > z3 - z2 + (z1 - z2)):
xc[0] += shift_x * x
xc[1] += shift_y * y
interface_c.update_xred()
interface_c = interface_c.return_atoms_to_cell()
interface_c = interface_c.make_neutral(oxidation = oxi, at_fixed = at_fixed, mode = 'equal',
silent = 1, return_oxidation = 0)
en = EwaldSummation(interface_c)
en = en.total_energy
if (en < en_min) and (x != 0) and (y != 0):
en_min = en
move_x = x
move_y = y
enable_print()
print(en)
diff = abs((en_min - en_prev) / max(abs(en_min), abs(en_prev)))
shift_x /= 2
shift_y /= 2
print("Interation: {}, difference: {}".format(itr, diff))
block_print()
interface_c = copy.deepcopy(interface)
for xc in interface_c.xcart:
if (xc[2] > z3 - z2 + (z1 - z2)):
xc[0] += shift_x * x
xc[1] += shift_y * y
interface_c.update_xred()
interface_c = interface_c.return_atoms_to_cell()
enable_print()
else:
pass
if (mode == "top") or (mode == "min"):
return interface_c, li_super_new
else:
raise ValueError("Wrong mode, check function's description.")
[docs]def insert(it_ins, ise_ins, mat_path, it_new, calc, type_of_insertion = "xcart" ):
"""For insertion of atoms to cells with changed lateral sizes
Input:
'type_of_insertion = xred' used to add xred coordinates
mat_path - path to geo files which are supposed to be changed
it_ins - already existed calculation; xred will be used from this calculation.
it_new - new folder in geo folder for obtained structure
This function finds version of calculation in folder mat_path and tries to use the same version of it_ins
"""
if not os.path.exists(mat_path):
print_and_log("Error! Path "+mat_path+" does not exist\n\n")
raise RuntimeError
if it_ins not in mat_path and it_ins not in it_new:
print_and_log('Cells are', it_ins, mat_path, it_new)
print_and_log("Error! you are trying to insert coordinates from cell with different name\n\n")
#raise RuntimeError
hstring = ("%s #on %s"% (traceback.extract_stack(None, 2)[0][3], datetime.date.today() ) )
if hstring != header.history[-1]: header.history.append( hstring )
geofilelist = runBash('find '+mat_path+'/target -name "*.geo*" ').splitlines()
if geofilelist == []:
print_and_log("Warning! Target folder is empty. Trying to find in root folder ...")
geofilelist = runBash('find '+mat_path+'/ -name "*.geo*" ').splitlines()
ins = None
for mat_geofile in geofilelist:
mat = CalculationVasp()
mat.name = mat_geofile
mat.read_geometry(mat_geofile)
#step = 0.27
#r_pore = 0.56
#r_mat = mat.hex_a / 2 - step
#pores = find_pores(mat.init, r_mat, r_pore, step, 0.3, 'central') #octahedral
#mat.xcart.append ( pores.xcart[0] )
#mat.typat.append(1)
try:
ins_working = ins
ins = calc[(it_ins, ise_ins, mat.version)]
except KeyError:
print_and_log( "No key", (it_ins, ise_ins, mat.version), "I use previous working version !!!", imp = 'y' )
ins = ins_working
#return
#ins.end.znucl = ins.znucl
#ins.end.nznucl = ins.nznucl
#ins.end.ntypat = ins.ntypat
#ins.end.typat = ins.typat
#print ins.xcart[-1]
mat_geopath = geo_folder+struct_des[it_new].sfolder + '/'
if type_of_insertion == "xcart":
#Please update here!
mat_filename = '/'+it_new+"."+"inserted."+str(mat.version)+'.'+'geo'
v = np.zeros(3)
result = insert_cluster(ins.end, v, mat.init, v )
mat.end = result
mat.init = result
# mat.znucl = mat.end.znucl
# mat.nznucl = mat.end.nznucl
# mat.ntypat = mat.end.ntypat
# mat.typat = mat.end.typat
# mat.natom = len(mat.end.xred)
#mat.version = ins.version
des = ins.name+" was inserted to "+mat_geofile
elif type_of_insertion == "xred":
mat_filename = '/from/'+it_new+".xred."+str(mat.version)+'.'+'geo'
#mat.end.rprimd = mat.rprimd
#mat.init.xred = copy.deepcopy(ins.end.xred)
#mat.init.typat = copy.deepcopy(ins.end.)
#print ins.end.xcart
rprimd = copy.deepcopy(mat.init.rprimd)
#build = mat.build
mat.init = copy.deepcopy(ins.end)
#mat.build = build
mat.init.rprimd = rprimd #return initial rprimd
mat.init.xred2xcart() #calculate xcart with new rprimd
des = "atoms with reduced coord. from "+ins.name+" was fully copied to "+mat_geofile
mat.init.name = 'test_insert_xred'+str(mat.version)
write_xyz(mat.init)
mat.path["input_geo"] = mat_geopath + it_new + mat_filename
if not mat.write_geometry("init",des): continue
print_and_log("Xred from "+it_ins+" was inserted in "+mat_geofile+" and saved as "+mat_filename+" \n\n")
return
[docs]def determine_voids(st, r_impurity, fine = 1, step_dec = 0.05):
if not r_impurity:
printlog('add_neb(): Error!, Please provide *r_impurity* (1.6 A?)')
sums = []
avds = []
printlog('Searching for voids', important = 'y')
st_pores = find_pores(st, r_matrix = 0.5, r_impurity = r_impurity, step_dec = step_dec, fine = fine, calctype = 'all_pores')
printlog('List of found voids:\n', np.array(st_pores.xcart) )
write_xyz(st.add_atoms(st_pores.xcart, 'H'), file_name = st.name+'_possible_positions')
write_xyz(st.add_atoms(st_pores.xcart, 'H'), replications = (2,2,2), file_name = st.name+'_possible_positions_replicated')
for x in st_pores.xcart:
# summ = local_surrounding(x, st, n_neighbours = 6, control = 'sum', periodic = True)
# avd = local_surrounding(x, st, n_neighbours = 6, control = 'av_dev', periodic = True)
summ, avd = local_surrounding2(x, st, n_neighbours = 6, control = 'sum_av_dev', periodic = True)
# print (summ, avd)
sums.append(summ)
avds.append(avd[0])
# print
sums = np.array(sums)
avds = np.array(avds).round(0)
print_and_log('Sum of distances to 6 neighboring atoms for each void (A):\n', sums, imp ='y')
print_and_log('Distortion of voids (0 - is symmetrical):\n', avds, imp ='y')
return st_pores, sums, avds
[docs]def determine_unique_voids(st_pores, sums, avds):
crude_prec = 1 # number of signs after 0
sums_crude = np.unique(sums.round(crude_prec))
print_and_log('The unique voids based on the sums:',
'\nwith 0.01 A prec:',np.unique(sums.round(2)),
'\nwith 0.1 A prec:',sums_crude,
imp ='y')
print_and_log('Based on crude criteria only', len(sums_crude),'types of void are relevant', imp = 'y')
insert_positions = []
start_table = []
for i, s in enumerate(sums_crude):
index_of_first = np.where(sums.round(crude_prec)==s)[0][0]
start_table.append([i, st_pores.xcart[index_of_first].round(2), index_of_first,
avds[index_of_first], sums[index_of_first] ])
insert_positions.append( st_pores.xcart[index_of_first] )
print_and_log( tabulate(start_table, headers = ['void #', 'Cart.', 'Index', 'Dev.', 'Sum'], tablefmt='psql'), imp = 'Y' )
return insert_positions
[docs]def insert_atom(st, el, i_void = None, i_void_list = None, r_imp = 1.6, ):
"""Simple Wrapper for inserting atoms
i_void (int) has higher priority than i_void_list
return st_new, i_add, sts_by_one
st_new - all positions are filled
i_add - the number of last inserted atom
sts_by_one - list of structures with only one inserted atom in all found positions
"""
r_impurity = r_imp
st_pores, sums, avds = determine_voids(st, r_impurity)
insert_positions = determine_unique_voids(st_pores, sums, avds)
printlog('To continue please choose *i_void* from the list above', imp = 'y')
# st.name = st.name.split('+')[0]
if i_void:
i_void_list = [i_void]
if i_void_list is None:
i_void_list = list(range(len(insert_positions)))
printlog('No i_void was provided, I insert all', imp = 'y')
st_new = st.copy()
sts_by_one = []
for i in i_void_list:
xc = insert_positions[i]
st_new, i_add = st_new.add_atoms([xc], el, return_ins = True)
st_one, _ = st.add_atoms([xc], el, return_ins = True)
st_one.name+='+'+el+str(i)
sts_by_one.append(st_one)
st_new.name+='+'+el+str(i)
st_new.des+=';Atom '+el+' added to '+ str(xc)
printlog(st.des, imp = 'y')
st_new.write_poscar()
st_new.magmom = [None]
return st_new, i_add, sts_by_one