我正在开发一个程序,我需要组合原子之间的距离或3D空间中的各个点。这是一个例子:
文件'测试'包含以下信息:
Ti 1.0 1.0 1.0
O 0.0 2.0 0.0
O 0.0 0.0 0.0
Ti 1.0 3.0 4.0
O 2.0 5.0 0.0
我希望我的代码计算点之间距离的所有组合(我已经完成了!),然后,我需要计算一个原子与另一个原子之间的距离小于2.2的次数。
这令人困惑,所以我会告诉你到目前为止我所得到的。
#!/usr/bin/env python
import sys, math, scipy, itertools
import numpy as np
try:
infile = sys.argv[1]
except:
print "Needs file name"
sys.exit(1)
#opening files for first part
ifile = open(infile, 'r')
coordslist = []
#Creating a file of just coordinates that can be 'mathed on'
for line in ifile:
pair = line.split()
atom = (pair[0]); x = float(pair[1]); y = float(pair[2]); z = float(pair[3])
coordslist += [(x,y,z)]
ifile.close()
#Define distance
def distance(p0,p1):
return math.sqrt((p0[0] - p1[0])**2 + (p0[1] - p1[1])**2 + (p0[2] - p1[2])** 2)
#Initializing for next section
dislist = []
bondslist = []
#Compute distances between all points 1-2, 1-3, 1-4, etc.
for p0, p1 in itertools.combinations(coordslist,2):
print p0, p1, distance(p0,p1)
dislist += [distance(p0, p1)]
if distance(p0,p1) < 2.2:
bondslist += [(p0, distance(p0,p1))]
print bondslist
print dislist
我不确定制作这些列表是否会对我有所帮助。到目前为止,他们还没有。
输出结果为:
(1.0, 1.0, 1.0) (0.0, 2.0, 0.0) 1.73205080757
(1.0, 1.0, 1.0) (0.0, 0.0, 0.0) 1.73205080757
(1.0, 1.0, 1.0) (1.0, 3.0, 4.0) 3.60555127546
(1.0, 1.0, 1.0) (2.0, 5.0, 0.0) 4.24264068712
(0.0, 2.0, 0.0) (0.0, 0.0, 0.0) 2.0
(0.0, 2.0, 0.0) (1.0, 3.0, 4.0) 4.24264068712
(0.0, 2.0, 0.0) (2.0, 5.0, 0.0) 3.60555127546
(0.0, 0.0, 0.0) (1.0, 3.0, 4.0) 5.09901951359
(0.0, 0.0, 0.0) (2.0, 5.0, 0.0) 5.38516480713
(1.0, 3.0, 4.0) (2.0, 5.0, 0.0) 4.58257569496
[((1.0, 1.0, 1.0), 1.7320508075688772), ((1.0, 1.0, 1.0), 1.7320508075688772), ((0.0, 2.0, 0.0), 2.0)]
[1.7320508075688772, 1.7320508075688772, 3.605551275463989, 4.242640687119285, 2.0, 4.242640687119285, 3.605551275463989, 5.0990195135927845, 5.385164807134504, 4.58257569495584]
我需要输出的一件事是每个原子的距离小于2.2的次数,例如:
1 2 (because atom 1 has two distances less than 2.2 associated with it)
2 2
3 2
4 0
5 0
我还需要看看两个原子的距离是否小于2.2。我这样做是为了计算鲍林的指控;这是你需要查看一个原子,确定它有多少键(距离小于2.2埃的原子),然后查看原子附着到那个原子,看看有多少原子附加到那些。这非常令人沮丧,但它总是依赖于跟踪每个原子而不仅仅是它们的组合。数组可能非常有用。
答案 0 :(得分:0)
在我们开始之前,请注意,如果是水晶(我有点怀疑你没有处理Ti2O3 分子),你应该小心周期性边界条件,即远离每个人的最后两个原子可能更接近相邻单元中的原子。
如果您知道要使用哪些工具,那么您尝试做的事情非常简单。您正在寻找能够告诉您集合中所有点之间的成对距离的方法。执行此操作的函数称为pdist
,scipy.spatial.distance.pdist
是准确的。这可以计算任何维度中任意点集的成对距离,具有任何类型的距离。在您的具体情况下,默认的欧几里德距离将会这样做。
一组点的成对矩阵距离(元素[i,j]
告诉您点i
和j
之间的距离)通过构造对称,对角线为零。出于这个原因,pdist
的通常实现只返回对角线一侧的非对角线元素,而scipy
的版本也不例外。然而,有一个方便的scipy.spatial.distance.squareform
函数将转换一个包含纯粹非对角线对称矩阵的浓缩版本的数组,并使其完整。从那里它很容易进行后期处理。
这就是我要做的事情:
import numpy as np
import scipy.spatial as ssp
# atoms and positions:
# Ti 1.0 1.0 1.0
# O 0.0 2.0 0.0
# O 0.0 0.0 0.0
# Ti 1.0 3.0 4.0
# O 2.0 5.0 0.0
# define positions as m*n array, where n is the dimensionality (3)
allpos = np.array([[1.,1,1], # 1. is lazy for dtype=float64
[0,2,0],
[0,0,0],
[1,3,4],
[2,5,0]])
# compute pairwise distances
alldist_condensed = ssp.distance.pdist(allpos) # vector of off-diagonal elements on one side
alldist = ssp.distance.squareform(alldist_condensed) # full symmetric distance matrix
# set diagonals to nan (or inf) to avoid tainting our output later
fancy_index = np.arange(alldist.shape[0])
alldist[fancy_index,fancy_index] = np.nan
# find index of "near" neighbours
thresh = 2.2
neighbslist = [np.where(alldist[k,:]<thresh)[0] for k in range(alldist.shape[0])] # the k'th element is an array containing the indices which are "close" to atom number k
# find total number of "near" neighbours
nearnum = [neighbs.size for neighbs in neighbslist] # the k'th element is the number of atoms which are "close" to atom number k
因此,对于您的具体情况,alldist
包含完整的距离矩阵:
array([[ nan, 1.73205081, 1.73205081, 3.60555128, 4.24264069],
[ 1.73205081, nan, 2. , 4.24264069, 3.60555128],
[ 1.73205081, 2. , nan, 5.09901951, 5.38516481],
[ 3.60555128, 4.24264069, 5.09901951, nan, 4.58257569],
[ 4.24264069, 3.60555128, 5.38516481, 4.58257569, nan]])
如您所见,我手动将对角线元素设置为np.nan
。这是必要的,因为我打算检查这个矩阵的元素小于thresh
,并且对角线中的零点肯定是合格的。在我们的情况下,np.inf
对于这些元素来说同样是一个不错的选择,但是如果你想从thresh
以外得到进一步的点呢?显然,对于那种情况-np.inf
或np.nan
是可以接受的(所以我选择了后者)。
近邻的后期处理使我们脱离了numpy的境界(你应该始终坚持numpy,只要你可以,这通常是最快的)。对于每个原子,您需要获得它附近的原子列表。嗯,这不是每个原子具有恒定长度的对象,因此您无法将其很好地存储在数组中。逻辑上的结论是使用list
,但是你可以使用所有python并使用列表推导来构建这个列表(从上面提醒):
neighbslist = [np.where(alldist[k,:]<thresh)[0] for k in range(alldist.shape[0])] # the k'th element is an array containing the indices which are "close" to atom number k
此处np.where
将找到行k
内的距离足够小的索引,并且1d索引数组存储在结果列表的k
元素中{ {1}}。然后,检查每个原子的这些数组的长度是微不足道的,为您提供近期内容的数量&#34;名单。请注意,我们可以将neighbslist
的输出转换为列表comp中的np.where
以完全保留numpy,但之后我们将不得不使用list
而不是{{ 1}}在下一行。
所以,你有两个关键变量,两个准确的列表; len(neighbs)
是&#34; near&#34;的数量。原子neighbs.size
的邻居nearnum[k]
中有k
,而k
是一个1d numpy数组,列出了原子range(allpos.shape[0])
的近索引,所以{{1} (对于neighbslist[k]
中的k
)neighbslist[k][j]
中的数字不等于j
。想想看,这个列表数组的结构可能有点丑陋,所以你应该在构造过程中将这个对象强制转换为适当的列表列表(即使这意味着一些开销)。
我最后才注意到你的输入数据在一个文件中。不用担心,也可以使用numpy轻松阅读!假设这些空行不在您的输入名称range(nearnum[k])
中,您可以调用
range(allpos.shape[0])
将位置矩阵读入变量。 k
选项允许test
忽略数据的第一列,这不是数字,会导致问题。无论如何,我们并不是真的需要它。