Externes Programm einbetten

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Guhrus_QUelltext
User
Beiträge: 2
Registriert: Dienstag 21. November 2006, 10:20

Hallo, ich bin blutiger anfänger in Python und schaffe es nicht eine allgemeine Lösung zu finden um ein externes Programm (in diesem Fall Tinker) anzuwenden. Ich denke es liegt daran, dass die Pfade ganz einfach nicht stimmen.

def getEnergy(tinkerfile, outfile):
os.system('C:/Programme/Tinker/bin/analyze < '+ tinkerfile +' > ' + outfile)
fp = open(outfile, 'r')
content = fp.readlines()
fp.close()

total = float(content[27].strip().split()[4])
return total

...später rufe ich es so auf....

getEnergy('input.tmp','ethan.tinker.energy')

Die Input und *.energy dateien sind in einem ganz anderen Ordner als das Programm. Gibt es vllt eine möglichkeit ganz allgemein durch einen Befehl das Verzeichniss der zwei dateien zu erzeugen und das dann in getEnergy einfügen??
Danke
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Zum TK Forum verschoben...
Bitte auch das http://www.python-forum.de/faq.php#21 beachten!

Ansonsten, herzlich willkommen im Forum! :lol:

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
BlackJack

Ich hab's mal wieder zurückverschoben. Tinker != TKinter. Hier ist wohl eher das TINKER Molecular Modeling Package gemeint.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Oh, das wusste ich nicht :oops:

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
BlackJack

Guhrus_QUelltext hat geschrieben:Die Input und *.energy dateien sind in einem ganz anderen Ordner als das Programm. Gibt es vllt eine möglichkeit ganz allgemein durch einen Befehl das Verzeichniss der zwei dateien zu erzeugen und das dann in getEnergy einfügen??
Verzeichnisse kann man mit `os.mkdir()` und `os.makedirs()` erzeugen. Mehrere Teile von Pfadnamen fügt man mit `os.path.join()` zusammen.

Ansonsten ist die Problembeschreibung noch etwas vage.
Guhrus_QUelltext
User
Beiträge: 2
Registriert: Dienstag 21. November 2006, 10:20

...das hab ich mir schon gedacht. nur sind meine kenntnisse wirklich so mies, dass ich einem wissenden nicht mal mein problem deutlich machen kann. Ich probiers trotzdem nochmal.
Also das Tinkerprogramm "Analyze" berechnet chemische Energien aus den Daten einer speziellen Tinker-Datei und gibt die Ergebnisse in einer neuen Datei aus.

.../analyze < input > output

in meinem Programm soll auf eine existierende Datei(normale .txt mit Koordinaten eines Moleküls) und deren Daten eine Transformation(Rotation der Koordinaten mit einer Drehmatrix) angewendet werden, die Ergebnisse dieser Transformation in eine neue Datei "input" geschrieben und mit dem Programm "Analyze" dann die chemischen Berechnungen in wieder einer neuen Datei "Output" ausgegeben werden. Es werden tatsächlich alle dateien erzeugt einschließlich der Output-datei, nur ist diese leer. Ich stell den Code einfach mal rein. auch für kleine Tipps bin ich sehr, sehr dankbar. Sorry, dass ich hier jetzt allet reinstelle, aber ich weiss nicht was wichtig ist, dass man mein anliegen versteht.

Code: Alles auswählen

[color=red]import sys
from math import sin, cos, pi, sqrt
from Numeric import *
import numpy
import os


def readCoordsTinker(filename):
    try:
        fp = open(filename, 'r')
        lines = fp.readlines()
        fp.close()
    except IOError:
        print 'File not found or read error'
        sys.exit()
    else:
        symbols=[]
        attype=[]
        conn=[]
        coords=[]
        info=[lines[0].split()]
        nat=int(info[0][0])
        for i in range(1,len(lines)):
            line=lines[i].split()
            temp=map(float, line[2:5])
            coords.append(temp)
            symbols.append(line[1])
            attype.append(line[5])
            conn.append(line[6:])
        return symbols,array(coords),attype,conn,nat


filename=raw_input("Name of .tinker.xyz file: ")
symbols,coords,attype,conn,nat=readCoordsTinker(filename) 
print "List of elements: ", symbols
print "Coordinates: ",array(coords)
print "Atomtyp:",attype
print "Konnektivität:",conn
print "Anzahl der Atome:",nat


def WriteCoordsTinker(file,nat,symbols,coords,attype,conn):
    out=open(file,"w")
    out.write(str(nat)+" tinker input mm3 params\n")
    for i in range(nat):
        string='%5i %s %12.8f %12.8f %12.8f %3i ' % \
                (i+1,symbols[i],coords[i][0],coords[i][1],coords[i][2],int(attype[i]))
        for j in range(len(conn[i])):
            string=string+'%4i '% (int(conn[i][j]))
        out.write(string+"\n")
    out.close()

def rotateCoords(coords,x1,x2,degree):
        degree= float(degree)
        n=array(x2)-array(x1)
	n=n/sqrt(dot(n,n))
	e0=cos(degree/2.0)
	e1=n[0]*sin(degree/2.0)
	e2=n[1]*sin(degree/2.0)
	e3=n[2]*sin(degree/2.0)
	ei=array([e0,e1,e2,e3])
	[eq0,eq1,eq2,eq3]=ei**2
	A=[[eq0+eq1-eq2-eq3,2*e1*e2+2*e0*e3,2*e1*e3-2*e0*e2],
          [2*e2*e1-2*e0*e3,eq0-eq1+eq2-eq3,2*e2*e3+2*e0*e1],
          [2*e3*e1+2*e0*e2,2*e3*e2-2*e0*e1,eq0-eq1-eq2+eq3]]
	ncoords=[]
	for i in range(len(coords)):
            if i in [0,1,3,4]:
                rotated=matrixmultiply(A,coords[i])
                ncoords.append(rotated)
            else:
                rotated=coords[i]
                ncoords.append(rotated)
        ncoords=array(ncoords)
        return ncoords


def createTinkerFile(inputfile, tinkerfile, field = 'mm2', mode = 'E'):
    fp = open(inputfile, 'w')
    fp.write(tinkerfile + '\n' + field + '\n' + mode)
    fp.close()


def getEnergy(tinkerfile, outfile):


    os.system('C:/Programme/Tinker/bin/analyze < '+ tinkerfile +' > ' + outfile)

    
    fp = open(outfile, 'r')
    content = fp.readlines()
    fp.close()

    total = float(content[27].strip().split()[4])
    return total



print
degree=float(raw_input("Drehwinkel in Grad: "))
degree=degree*2*pi/360
print
Schrittanzahl=int(raw_input("Anzahl der Schritte: "))
Schritt=degree/Schrittanzahl
nwinkel=Schritt
input_empty='input.tmp'

for i in range(Schrittanzahl):
    ncoords=rotateCoords(coords,coords[0],coords[2],nwinkel)
    nwinkel=nwinkel+Schritt
    print i+1,"."
    print ncoords
    WriteCoordsTinker("ethan.tinker.tmp",nat,symbols,ncoords,attype,conn)
    createTinkerFile(input_empty, "ethan.tinker.tmp")
    getEnergy('input.tmp','ethan.tinker.energy')[/color]
BlackJack

Hm, was mir so auffällt ist, dass Du Daten in eine Datei 'ethan.tinker.tmp' schreibst, ``analyze`` dann aber mit 'input.tmp' fütterst. Ausserdem machst Du nichts mit dem Rückgabewert von `getEnergy()` und die Funktion müsste eine Ausnahme auslösen wenn die Datei wirklich leer ist. Du greifst auf Zeile 27 bzw. 28 zu, das gibt einen `IndexError` wenn in `content` nicht genug Zeilen wären.

Edit: Und noch ein paar Anmerkungen

Es gibt zwei Stellen bei denen die Einrückung fehlerhaft ist und *viele* bei denen die Einrückung inkonsistent ist. Empfohlen sind 4 Leerzeichen pro Ebene und keine Tab-Zeichen in der Datei.

Ein paar Zeilen sind länger als 80 Zeichen. Und der Style-Guide empfielt Leerzeichen um Operatoren und Zuweisungen und Namen im kleine_worte_mit_unterstrichen-Stil für alles ausser Klassen und Konstanten.

Sternchen-``import`` ist böse. In `Numeric` ist zum Beispiel eine Funktion `sum()` die beim Sternchen-``import`` die gleichnamige eingebaute ersetzt. So etwas kann zu unangenehmen Überraschungen führen. In `Numeric` sind ca. 170 Namen definiert von denen nur drei benutzt werden.

Auf Modulebene werden Programmcode mit Funktionsdefinitionen vermischt. Das ist unübersichtlich. Und Code auf Modulebene wird immer ausgeführt wenn man das Modul importiert. Man kann es also nicht im Interpretierer importieren um einzelne Funktionen zu testen.

Einen Index zu benutzen um über die Elemente einer oder mehrerer Listen zu iterieren ist "unpythonisch". In Python iteriert man stattdessen direkt über die Elemente. Falls man zusätzlich einen Index benötigt, kann man `enumerate()` benutzen. Der Quelltext wird dadurch in den meisten Fällen verständlicher weil anstelle eines Index ein (hoffentlich) aussagekräftiger Name tritt.

Beim Einlesen in `readCoordsTinker()` wird die erste Zeile anders behandelt, das kann man lösen indem man sich einen Iterator geben lässt und vor der Schleife die erste Zeile mit der `next()`-Methode holt. `info` wird übrigens unnötigerweise in eine Liste gesteckt. Man könnte die Schleife so schreiben, und an der Stelle schon alle Zeichenketten in die entsprechenden Typen umwandeln:

Code: Alles auswählen

        lines = iter(lines)
        nat = lines.next().split()[0]
        for line in lines:
            elements = line.split()
            coords.append(map(float, elements[2:5]))
            symbols.append(elements[1])
            attype.append(map(int, elements[5]))
            conn.append(map(int, elements[6:]))
Die ganzen Listen werden immer zusammen im Programm herumgereicht, was ein gutes Zeichen dafür ist, dass es keine unabhängigen Daten sind. Im Grunde scheinen die Elemente an einem Index zusammen zu gehören. Statt diese in "parallelen" Listen zu speichern, könnte man sie zu einem Objekt zusammenfassen und das dann in einer Liste speichern und kann diese Liste zurückgeben bzw. als Argumente übergeben.

Was soll eigentlich passieren wenn die Information über die Anzahl der Datensätze nicht mit der Information in der ersten Zeile übereinstimmt? Wenn die Anzahlen übereinstimmen, dann ist die Information in `nat` redundant, weil ``nat == len(coords)`` oder `len()` von einer der anderen Listen gilt.

Angenommen man hätte eine Liste mit einem Objekt pro Zeile (Atom?) dann könnte die Schleife beim Schreiben in eine Datei so aussehen:

Code: Alles auswählen

    for i, atom in enumerate(atoms):
        coord_str = ' '.join('%12.8f' % c for c in atom.coords)
        conn_str = ' '.join('%4i' % c for c in atom.conn)
        out.write('%5i %s %s %3i %s\n'
                  % (i + 1, atom.symbol, coord_str, atom.attype, conn_str))
Zum Umrechnen von Grad in Radians gibt's `math.radians()`.

Je grösser die Schrittanzahl um so ungenauer wird der jeweilige Winkel weil sich bei jeder Addition eine kleine Ungenauigkeit akkumuliert. Anstelle immer einen `Schritt` zu addieren kann man `Schritt` mit der aktuellen Schrittnummer multiplizieren.
Antworten