2013-10-10 82 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:現在它似乎按照我希望的方式執行。我將不得不使用不同大小的輸入文件來查看它是否仍然符合我的預期。 –