From Fortran to Python - Running inside your browser.
Dieser kleine Rechner ist ein direkter Port eines ursprünglich in Fortran 95 geschriebenen Programms, um das HF-Programm direkt im Browser laufen zu lassen. Dazu wird PyScript verwendet. Teile des Codes sind etwas unkonventionell strukturiert, weil der Python-Code direkt auf einem Fortran-Programm basiert.
Das Programm parst zunächst die Input-Datei (welche für das Fortran-Programm als h2.in gespeichert wurde). Diese
beginnt mit einer Kommentar-Zeile (welche übersprungen wird), in der darauffolgenden Zeile steht die Bindungslänge
(in atomaren Einheiten, also Bohr und NICHT in Ångström).
Es folgt eine weitere Kommentarzeile (in Fortran-Logik auskommentiert durch ein "!"), dann wird der Basissatz im
Gaussian-Format eingelesen, so, wie es beispielsweise auf
basissetexchange.org zu finden ist. Eine typische Zeile in diesem Format sieht wie folgt aus:
0.1873113696D+02
Das ist eine alte Schreibweise für Gleitkommazahlen mit doppelter Genauigkeit; der Buchstabe D steht für
den Exponenten zur Basis 10. D+02 bedeutet $10^2$. Die Zahl entspricht somit 18.73113696.
Da moderne Programmiersprachen wie Python (selbst Excel!!) standardmäßig das "E" verwenden (z.B. 1.8E+01),
führt der Parser eine Text-Ersetzung durch, bevor die Zahlen als numerische Werte in die dynamischen
Arrays geladen werden.
Wichtig im ursprünglichen Code war die Verwaltung des Arbeitsspeichers. In Fortran 77 mussten Array-Größen
quasi zur Kompilierzeit feststehen (wir befinden uns hier noch in der Lochkarten-Ära). Mit Fortran 90 wurde die dynamische Speicherverwaltung
(ALLOCATABLE) eingeführt. Python macht das praktischerweise automatisch:
Listen und NumPy-Arrays passen sich dynamisch der Größe des eingelesenen Basissatzes an, egal ob
STO-3G oder 6-311++G verwendet wird. Anzumerken ist, das die aktuelle Version des Codes nur s-Orbitale einlesen kann.
Die Berechnung der 1- und 2-Elektronenintegrale ist der eigentlich spannende Teil im HF. Da die Basissätze (wie z.B. STO-3G oder 6-311++G) aus kontrahierten Gauss-Funktionen bestehen, können die Integrale nicht direkt berechnet werden. Stattdessen wird in einer Schleifenstruktur über die Basisfunktionen iteriert und innerhalb dieser über die zugrundeliegenden primitiven Gauss-Funktionen ($\phi_{prim} \propto e^{-\alpha r^2}$). Die Berechnung erfolgt rein analytisch unter Ausnutzung des Gaussian Product Theorems.
Diese Integrale beschreiben die Bewegung eines einzelnen Elektrons im Feld der Kerne.
Sie werden in der Funktion integrale_berechnen berechnet und füllen die Matrizen S, T und V.
Jedes Matrixelement (z.B. $S_{\mu\nu}$) ist die Summe über alle Primitiven $i$ (aus $\mu$) und $j$ (aus $\nu$):
$S_{\mu\nu} = \sum_{i,j} d_i d_j N_i N_j \cdot \text{overlap}( \alpha_i, \alpha_j, ... )$
Das Integral eri_calc_func berechnet die Abstoßung zwischen zwei Elektronenwolken:
$(\mu\nu | \lambda\sigma) = \int \int \phi_\mu(1)\phi_\nu(1) \frac{1}{r_{12}} \phi_\lambda(2)\phi_\sigma(2) d\tau_1 d\tau_2$.
boys(x) zum Einsatz, um den $1/r_{12}$ Operator zu integrieren.
Der Code nutzt scipy.special.erf für maximale numerische Präzision, fängt aber den Fall $x \approx 0$ (kleine Abstände)
durch eine Taylor-Entwicklung ($1 - x/3$) ab, um Divisionen durch Null zu vermeiden.
Der eigentliche Programmablauf folgt exakt den 12 logischen Schritten aus der Vorlesung:
numpy.linalg.eigh (basierend auf LAPACK),
was effizienter ist (und mir beim porten Kopfschmerzen erspart hat). Dies liefert die
Orbitalenergien $\epsilon$ und die Koeffizienten $C'$.
Da es sich komisch anfühlt, den rohen Code dieser Website noch mal auf die Website selbst zu setzen, hier stattdessen der Versuch, den Fortran-turned-Python-Code wieder zu Bleistift-und-Papier Quantenmechanik zu übersetzen. Wir betrachten das System, das oben via dem Beispiel-Input gegeben ist: H$_2$ ($R=1.4$ Bohr), aber mit der minimalen STO-3G-Basis. Die Indizes $\mu, \nu, \lambda, \sigma$ laufen über die kontrahierten Basisfunktionen (AO) $\chi$.
Konstruktion der Basisfunktionen $\chi_\mu$ durch Kontraktion von 3 primitiven Gauß-Funktionen ($K=3$). Für das Wasserstoffatom $A$ bei $\boldsymbol{R}_A$:
$$ \chi_1(\boldsymbol{r}) = \sum_{k=1}^{3} d_{k1} \left(\frac{2\alpha_{k1}}{\pi}\right)^{3/4} e^{-\alpha_{k1} |\boldsymbol{r} - \boldsymbol{R}_A|^2} $$
Hier entsprechen basis_alpha den Exponenten $\alpha$ und basis_d den Kontraktionskoeffizienten $d$.
integrale_berechnen)Die Funktion eri_calc_func nutzt hier explizit Boys-Funktionen, um das $1/r_{12}$ Integral analytisch zu lösen.
Lösen des Eigenwertproblems der Überlappungsmatrix:
$$ \boldsymbol{S}\boldsymbol{U} = \boldsymbol{U}\boldsymbol{s} \quad (\boldsymbol{s} \text{ ist diagonal}) $$Konstruktion der Transformationsmatrix $\boldsymbol{X} = \boldsymbol{S}^{-1/2}$:
$$ \boldsymbol{X} = \boldsymbol{U} \boldsymbol{s}^{-1/2} \boldsymbol{U}^T $$Dies transformiert die nicht-orthogonale AO-Basis in eine orthogonale Basis.
Beginn des iterativen Verfahrens zur Lösung der Roothaan-Hall-Gleichungen (und gleichzeitig Begrenzung der Maximalen Zyklenanzahl).
Konstruktion der Fock-Matrix $\boldsymbol{F}$ aus dem Einteilchen-Hamiltonian und dem mittleren Feld der Elektronen (Coulomb $\boldsymbol{J}$ und Austausch $\boldsymbol{K}$):
$$ F_{\mu\nu} = H_{\mu\nu}^{\text{core}} + \sum_{\lambda\sigma} P_{\lambda\sigma} \left[ (\mu\nu|\lambda\sigma) - \frac{1}{2} (\mu\lambda|\nu\sigma) \right] $$Dies entspricht in Matrixform $\boldsymbol{F} = \boldsymbol{H}^{\text{core}} + \boldsymbol{G}(\boldsymbol{P})$.
Transformation in die orthogonale Basis:
$$ \boldsymbol{F}' = \boldsymbol{X}^T \boldsymbol{F} \boldsymbol{X} $$Diagonalisierung der Fock-Matrix (Lösung der Pseudo-Eigenwertgleichung):
$$ \boldsymbol{F}' \boldsymbol{C}' = \boldsymbol{C}' \boldsymbol{\epsilon} $$$\boldsymbol{\epsilon}$ enthält die Orbitalenergien, $\boldsymbol{C}'$ die MO-Koeffizienten in der orthogonalen Basis.
Rücktransformation in die ursprüngliche AO-Basis:
$$ \boldsymbol{C} = \boldsymbol{X} \boldsymbol{C}' $$Das Molekülorbital $\psi_i$ ist nun definiert als $\psi_i = \sum_\mu C_{\mu i} \chi_\mu$.
Aufbau der neuen Dichtematrix $\boldsymbol{P}$ für einen geschlossenschaligen Singulett-Zustand (RHF). Summation über die besetzten Orbitale (hier nur $a=1$):
$$ P_{\mu\nu} = 2 \sum_{a}^{N_{elec}/2} C_{\mu a} C_{\nu a}^* $$Berechnung der elektronischen Energie als Erwartungswert. Man beachte, dass $\boldsymbol{F} = \boldsymbol{H}^{\text{core}} + \boldsymbol{G}$ ist, wodurch die Doppelzählung der Elektron-Elektron-Wechselwirkung korrigiert wird:
$$ E_{elec} = \frac{1}{2} \text{Spur}\left( \boldsymbol{P} (\boldsymbol{H}^{\text{core}} + \boldsymbol{F}) \right) $$