2013-10-10 46 views
1

我对编程和Python非常陌生,我试图将DLPOLY HISTORY文件转换为弧形文件。我需要做的是提取晶格向量(在timestep这个单词下的3x3数组),x,y和z坐标(每个元素下面的行上的三个入口)和电荷(线上的第四个入口与元件)。For循环播放Python中的文本文件的部分

理想情况下,我想最终能够转换任意大小和帧长度的文件。

两个标题行和DLPOLY历史文件的头两帧,看起来像这样:

File Title 
     0   3   5     136     1906 
timestep   0   5 0 3   0.000500   0.000000 
     3.5853000000  0.0000000000  0.0000000000 
     -1.7926500000  3.1049600000  0.0000000000 
     0.0000000000  0.0000000000  4.8950000000 
Ca    1 40.078000 1.050000 0.000000 
    0.000000000   0.000000000   0.000000000 
O    2 15.999400 -0.950000 0.000000 
    1.792650000  -1.034986100   1.140535000 
H    3 1.007940 0.425000 0.000000 
    1.792650000  -1.034986100   1.933525000 
O    4 15.999400 -0.950000 0.000000 
    -1.792650000   1.034987000  -1.140535000 
H    5 1.007940 0.425000 0.000000 
    -1.792650000   1.034987000  -1.933525000 
timestep  10   5 0 3   0.000500   0.005000 
     3.5853063513  0.0000000000  0.0000000000 
     -1.7926531756  3.1049655004  0.0000000000 
     0.0000000000  0.0000000000  4.8950086714 
Ca    1 40.078000 1.050000 0.020485 
    -0.1758475885E-01 0.1947928245E-04 -0.1192033544E-01 
O    2 15.999400 -0.950000 0.051020 
    1.841369991  -1.037431082   1.120698646 
H    3 1.007940 0.425000 0.416965 
    1.719029690  -1.029327936   2.355541077 
O    4 15.999400 -0.950000 0.045979 
    -1.795057186   1.034993005  -1.093028694 
H    5 1.007940 0.425000 0.373772 
    -1.754959531   1.067269072  -2.320776528 

到目前为止我的代码是:

fileList = history_file.readlines() 
number_of_frames = int(fileList[1].split()[3]) 
number_of_lines = int(fileList[1].split()[4]) 
frame_length = (number_of_lines - 2)/number_of_frames 
number_of_atoms = int(fileList[1].split()[2]) 
lines_per_atom = frame_length/number_of_atoms 

for i in range(3, number_of_lines+1, frame_length): 

#maths for converting lattice vectors 
#print statement to write out converted lattice vectors 

    for j in range(i+3, frame_length+1, lines_per_atom): 
      atom_type = fileList[j].split()[0] 
      atom_x = fileList[j+1].split()[0] 
      atom_y = fileList[j+1].split()[1] 
      atom_z = fileList[j+1].split()[2] 
      charge = fileList[j].split()[3] 
      print atom_type, atom_x, atom_y, atom_z, charge 

我可以提取和转换格矢量,这不是一个问题。然而,当涉及到第二个for循环只执行一次,它认为我的范围内结束发言

frame_length+1 

是不正确的,但如果我把它改为

i+3+frame_length+1 

我得到以下错误:

​​

我认为这意味着我要结束一个数组。

我敢肯定,我忽略了一些非常简单的东西,但任何帮助将不胜感激。

我也想知道是否有更高效的阅读文件的方式,因为据我所知,readlines将整个文件读入内存,HISTORY文件可以轻松达到几GB的大小。

回答

1

好的,我们可以使用您提供的示例值来查找问题,进行相当简单的检查。如果我们输入以下代码

for i in range(3,1907,136): 
    for j in range(i+3,137,2): 
     print i,j 

我们得到这样的:

3 6 
3 8 
3 10 
... 
3 132 
3 134 
3 136 

这是您遇到的错误。循环似乎只重复一次。但是,如果我们稍微更改代码,则会看到问题的根源。如果我们运行

for i in range(3,1907,136): 
    print "i:", i, 
    for j in range(i+3,137,2): 
     print "j:", j 

我们得到这样的:

i: 3 j: 6 
j: 8 
j: 10 
j: 12 
... 
j: 134 
j: 136 
i: 139 i: 275 i: 411 i: 547 i: 683 i: 819 i: 955 i: 1091 i: 1227 i: 1363 i: 1499 
i: 1635 i: 1771 

所以,你可以看到内部循环(j循环)第一次运行时,一旦其完成,外环(i循环)运行一路通过,而不让内层循环去除。这是因为你在内循环上设置了range。在第一次运行时,它的计算结果为range(3,137,2),但第二次运行时出现在range(142,137,2),因为i在第二次运行时从139开始。它在启动之前已经终止。

为了得到你想要的东西(或什么,我想你想要的)是本作内部循环:

for j in range(4,frame_length,line_per_atom): 
    atom_type = fileList[j+i].split()[0] 

这使得j线的每一帧中的迭代过去4号线

但是我还没弄清楚的是你的代码是如何工作的。我手中计算了您的示例中的值作为检查。

frame_length = (1906 - 2)/136 = 14 
lines_per_atom = 14/5 = 2.8 

2.8 lines_per_atom是非法的,它必须是一个整数,我不知道你是如何没有得到TypeError。 lines_per_atom的计算应该是lines_per_atom = (frame_length - 4)/number_of_atoms

无论如何,希望这可以工作!

(另外,尝试在未来,而不是下划线使用驼峰的变量名。所以lines_per_atom变得linesPerAtom,更容易输入在我看来)

+0

谢谢,我没赶上与lines_per_atom计算错误。奇怪的是,无论有没有更正,它仍然会给出我期待的2的结果。我想知道这是为什么? –

+0

我也尝试了你对第二个循环的建议修正,但是我得到以下错误:charge = fileList [j + i] .split()[3] IndexError:列表索引超出范围。 –

+0

我将第二个循环更改为范围(i + 3,frame_length + i-2,lines_per_atom)中的j:现在它似乎按照我希望的方式执行。我将不得不使用不同大小的输入文件来查看它是否仍然符合我的预期。 –