108 KiB
Installation de QCaml¶
- Clonage de QCaml:
git clone https://gitlab.com/scemama/QCaml.git
- Installation des dependances:
opam install ocamlbuild ocamlfind lacaml gnuplot getopt alcotest zarith cd QCaml ./configure
- Compilation
make
Liens utiles¶
- Documentation des modules : https://sanette.github.io/ocaml-api/4.10/index.html
- Lacaml : http://mmottl.github.io/lacaml/api/lacaml/
- Gnuplot : https://github.com/c-cube/ocaml-gnuplot
Initialisation¶
Bloc a executer avant de pouvoir utiliser QCaml dans le Notebook
let png_image = print_endline ;;
(* --------- *)
(*Mettre le bon chemin ici *)
#cd "/home/ydamour/QCaml";;
#use "topfind";;
#require "jupyter.notebook";;
#require "gnuplot";;
let png_image name =
Jupyter_notebook.display_file ~base64:true "image/png" ("Notebooks/images/"^name)
;;
#require "lacaml.top";;
#require "alcotest";;
#require "str";;
#require "bigarray";;
#require "zarith";;
#require "getopt";;
#directory "_build";;
#directory "_build/Basis";;
#directory "_build/CI";;
#directory "_build/MOBasis";;
#directory "_build/Nuclei";;
#directory "_build/Parallel";;
#directory "_build/Perturbation";;
#directory "_build/SCF";;
#directory "_build/Utils";;
#load "Constants.cmo";;
#load_rec "Util.cma";;
#load_rec "Matrix.cmo";;
#load_rec "Simulation.cmo";;
#load_rec "Spindeterminant.cmo";;
#load_rec "Determinant.cmo";;
#load_rec "HartreeFock.cmo";;
#load_rec "MOBasis.cmo";;
#load_rec "F12CI.cmo";;
#install_printer AngularMomentum.pp_string ;;
#install_printer Basis.pp ;;
#install_printer Charge.pp ;;
#install_printer Coordinate.pp ;;
#install_printer Vector.pp;;
#install_printer Matrix.pp;;
#install_printer Util.pp_float_2darray;;
#install_printer Util.pp_float_array;;
#install_printer Util.pp_matrix;;
#install_printer HartreeFock.pp ;;
#install_printer Fock.pp ;;
#install_printer MOClass.pp ;;
#install_printer DeterminantSpace.pp;;
#install_printer SpindeterminantSpace.pp;;
#install_printer Bitstring.pp;;
let pp_mo ppf t = MOBasis.pp ~start:1 ~finish:0 ppf t ;;
#install_printer pp_mo;;
(* --------- *)
open Lacaml.D
Calculs¶
H$_2$O¶
(*
let xyz_string =
"3
Water
O 0. 0. 0.
H -0.756950272703377558 0. -0.585882234512562827
H 0.756950272703377558 0. -0.585882234512562827
"
*)
H$_4$¶
(* Fonction création chaîne linéaire de n H *)
let xyz d n =
let accu = ""
in
let rec toto accu d n =
let accu =
if n=0
then accu ^ ""
else
match n with
| 1 -> "H 0. 0. 0.\n" ^ accu
| x -> toto ("H" ^ " " ^ string_of_float( d *. float_of_int(n-1)) ^ " 0. 0.\n" ^ accu ) d (n-1)
in
accu
in string_of_int(n) ^ "\nH" ^ string_of_int(n) ^ "\n" ^ toto accu d n;;
let xyz_string = xyz 1.8 6;;
Base atomique¶
Les bases atomiques sont telechargeables ici: https://www.basissetexchange.org/
On telecharge la base cc-pvdz depuis le site:
let basis_string =
"
HYDROGEN
S 6
1 0.3552322122E+02 0.9163596281E-02
2 0.6513143725E+01 0.4936149294E-01
3 0.1822142904E+01 0.1685383049E+00
4 0.6259552659E+00 0.3705627997E+00
5 0.2430767471E+00 0.4164915298E+00
6 0.1001124280E+00 0.1303340841E+00
"
(*
let basis_string =
"
HYDROGEN
S 4
1 1.301000E+01 1.968500E-02
2 1.962000E+00 1.379770E-01
3 4.446000E-01 4.781480E-01
4 1.220000E-01 5.012400E-01
S 1
1 1.220000E-01 1.000000E+00
P 1
1 7.270000E-01 1.0000000
OXYGEN
S 9
1 1.172000E+04 7.100000E-04
2 1.759000E+03 5.470000E-03
3 4.008000E+02 2.783700E-02
4 1.137000E+02 1.048000E-01
5 3.703000E+01 2.830620E-01
6 1.327000E+01 4.487190E-01
7 5.025000E+00 2.709520E-01
8 1.013000E+00 1.545800E-02
9 3.023000E-01 -2.585000E-03
S 9
1 1.172000E+04 -1.600000E-04
2 1.759000E+03 -1.263000E-03
3 4.008000E+02 -6.267000E-03
4 1.137000E+02 -2.571600E-02
5 3.703000E+01 -7.092400E-02
6 1.327000E+01 -1.654110E-01
7 5.025000E+00 -1.169550E-01
8 1.013000E+00 5.573680E-01
9 3.023000E-01 5.727590E-01
S 1
1 3.023000E-01 1.000000E+00
P 4
1 1.770000E+01 4.301800E-02
2 3.854000E+00 2.289130E-01
3 1.046000E+00 5.087280E-01
4 2.753000E-01 4.605310E-01
P 1
1 2.753000E-01 1.000000E+00
D 1
1 1.185000E+00 1.0000000
"
*)
Une orbitale atomique centree sur l'atome A est composee d'une contraction de m Gaussiennes: $$ \chi(r) = \sum_{i=1}^m a_i \exp \left( -\alpha_i |r-r_A|^2 \right) $$ Dans le fichier de base, la 2ieme colonne represente l'exposant $\alpha_i$ et la 3ieme colonne le coefficient de contraction $a_i$.
let nuclei =
Nuclei.of_xyz_string xyz_string
let basis =
Basis.of_nuclei_and_basis_string nuclei basis_string
let simulation =
Simulation.make ~charge:0 ~multiplicity:1 ~nuclei basis
let ao_basis =
Simulation.ao_basis simulation
Plot des orbitales atomiques¶
let plot data filename =
let output = Gnuplot.Output.create (`Png filename) in
let gp = Gnuplot.create () in
Gnuplot.set gp ~use_grid:true;
List.map Gnuplot.Series.lines_xy data
|> Gnuplot.plot_many gp ~output;
Gnuplot.close gp ;
Jupyter_notebook.display_file ~base64:true "image/png" filename
;;
let x_values =
let n = 1000 in
let xmin, xmax =
let coord =
Array.map snd nuclei
|> Array.map (fun a -> Coordinate.(get X) a)
in
Array.sort compare coord;
coord.(0) -. 4. ,
coord.(Array.length coord -1) +. 4.
in
let dx =
(xmax -. xmin) /. (float_of_int n -. 1.)
in
Array.init n (fun i -> xmin +. (float_of_int i)*.dx)
in
let data =
Array.map (fun x ->
let point = Coordinate.make_angstrom
{ Coordinate.
x ; y = 0. ; z = 0.
} in
AOBasis.values ao_basis point
) x_values
|> Mat.of_col_vecs
|> Mat.transpose_copy
|> Mat.to_col_vecs
|> Array.map Vec.to_list
|> Array.map (fun l -> List.mapi (fun i y -> (x_values.(i),y)) l)
|> Array.to_list
in
plot data "test_data.png"
Calcul Hartree-Fock¶
let hf = HartreeFock.make simulation
let mo_basis = MOBasis.of_hartree_fock hf
Orbitales moleculaires : $$ \phi_j(r) = \sum_{i=1}^{N_b} C_{ij} \chi_i(r) $$
- $i$: lignes
- $j$: colonnes
Extraction des OM de la structure de donnees mo_basis
comme une matrice $C$ utilisable avec Lacaml:
let mo_coef = MOBasis.mo_coef mo_basis
let plot data filename =
let output = Gnuplot.Output.create (`Png filename) in
let gp = Gnuplot.create () in
Gnuplot.set gp ~use_grid:true;
List.map Gnuplot.Series.lines_xy data
|> Gnuplot.plot_many gp ~output;
Gnuplot.close gp ;
Jupyter_notebook.display_file ~base64:true "image/png" filename
;;
let x_values =
let n = 1000 in
let xmin, xmax =
let coord =
Array.map snd nuclei
|> Array.map (fun a -> Coordinate.(get X) a)
in
Array.sort compare coord;
coord.(0) -. 4. ,
coord.(Array.length coord -1) +. 4.
in
let dx =
(xmax -. xmin) /. (float_of_int n -. 1.)
in
Array.init n (fun i -> xmin +. (float_of_int i)*.dx)
in
let data =
let result =
Array.map (fun x ->
let point = Coordinate.make_angstrom
{ Coordinate.
x ; y = 0. ; z = 0.
} in
MOBasis.values mo_basis point
) x_values
|> Mat.of_col_vecs
|> Mat.transpose_copy
|> Mat.to_col_vecs
|> Array.map Vec.to_list
|> Array.map (fun l -> List.mapi (fun i y -> (x_values.(i),y)) l)
in
[ result.(0) ; result.(1) ; result.(2) ]
in
plot data "test_data.png"
Calcul Hartree-Fock a la main¶
Methode¶
$$ \hat{H} = \hat{T} + \hat{V}^{\text{NN}} + \hat{V}^{\text{eN}} + \hat{V}^{\text{ee}} $$On exprime les differentes quantites dans la base des orbitales atomiques pour chacun des termes:
\begin{eqnarray} T_{ij} & = & \langle \chi_i | \hat{T} | \chi_j \rangle = \iiint \chi_i(\mathbf{r}) \left( -\frac{1}{2} \Delta \chi_j(\mathbf{r}) \right) d\mathbf{r} \\ V^{NN} & = & \text{constante} \\ V^{eN}_{ij} & = & \langle \chi_i | \hat{V}^{\text{eN}} | \chi_j \rangle = \sum_A \iiint \chi_i(\mathbf{r}) \frac{-Z_A}{|\mathbf{r} - \mathbf{R}_A|} \chi_j(\mathbf{r}) d\mathbf{r} \\ V^{ee}_{ijkl} & = & \langle \chi_i \chi_j | \hat{V}^{\text{ee}} | \chi_k \chi_l \rangle = \iiint \iiint \chi_i(\mathbf{r}_1) \chi_j(\mathbf{r}_2) \frac{1}{|\mathbf{r}_1 - \mathbf{r}_2|} \chi_k(\mathbf{r}) \chi_l(\mathbf{r}) d\mathbf{r}_1 d\mathbf{r}_2 \\ \end{eqnarray}ou $\mathbf{R}_A$ est la position du noyau $A$ et $Z_A$ sa charge.
Dans la methode Hartree-Fock, l'electron 1 ne "voit" pas directement l'electron 2, mais il voit l'electron 2 comme une densite de charge.
On definit donc 2 operateurs pour chaque orbitale:
- Coulomb : $$ \hat{J}_j \chi_i(\mathbf{r}_1) = \chi_i(\mathbf{r}_1) \iiint \frac{1}{|\mathbf{r}_1 - \mathbf{r}_2|} |\chi_j(\mathbf{r}_2)|^2 d \mathbf{r}_2 $$
- Echange : $$ \hat{K}_j \chi_i(\mathbf{r}_1) = \chi_j(\mathbf{r}_1) \iiint \frac{1}{|\mathbf{r}_1 - \mathbf{r}_2|} \chi_i(\mathbf{r}_2) \chi_j(\mathbf{r}_2) d \mathbf{r}_2 $$ et on n'a plus que des operateurs a 1 electron.
ou $P$ est la matrice densite definie comme $$ P_{ij} = \sum_{k=1}^{N_{\text{occ}}} 2 C_{ik} C_{kj} $$ et $C$ est la matrice des coefficients des orbitales moleculaires exprimees dans la base des orbitales atomiques.
Une orbitale moleculaire est une combinaison lineaire d'orbitale atomique: $$ \phi_k(\mathbf{r}) = \sum_i C_{ik} \chi_i(\mathbf{r}) $$
Les orbitales moleculaires sont toutes orthogonales entre elles, et normalisees a 1.
La methode Hartree-Fock permet d'obtenir les orbitales moleculaires qui minimisent l'energie quand la fonction d'onde est exprimee comme un determinant de Slater: $$ \Psi(\mathbf{r}_1,\dots,\mathbf{r}_N) = \left| \begin{array}{ccc} \phi_1(\mathbf{r}_1) & \dots & \phi_N(\mathbf{r}_1) \\ \vdots & \ddots & \vdots \\ \phi_1(\mathbf{r}_N) & \dots & \phi_N(\mathbf{r}_N) \end{array}\right| $$
Application¶
On commence par creer une base orthogonale $X$ a partir des OA
let m_X =
AOBasis.ortho ao_basis
Pour pouvoir calculer les matrices $J$ et $K$, il faut avoir une matrice densite. On part donc avec une matrice densite d'essai. Une facon de faire est de diagonaliser l'Hamiltonien sans $V_{ee}$.
let nocc = 1 (* On a 10 electrons, donc 5 orbitales occupees. *)
let c_of_h m_H =
(* On exprime H dans la base orthonormale *)
let m_Hmo =
Util.xt_o_x m_H m_X (* H_mo = X^t H X *)
in
(* On diagonalise cet Hamiltonien *)
let m_C', _ =
Util.diagonalize_symm m_Hmo
in
(* On re-exprime les MOs dans la base des AOs (non-orthonormales) *)
gemm m_X m_C' (* C = X.C' *)
let m_C =
match Guess.make ~nocc ~guess:`Hcore ao_basis with
| Hcore m_H -> c_of_h m_H
| _ -> assert false
On construit la matrice densite
let m_P =
(* P = 2 C.C^t *)
gemm ~alpha:2. ~transb:`T ~k:nocc m_C m_C
On construit les matrices $H_c = T+V_{\text{eN}}$, et $J$ et $K$:
let m_Hc, m_J, m_K =
let f =
Fock.make_rhf ~density:m_P ao_basis
in
Fock.(core f, coulomb f, exchange f)
On construit l'operateur de Fock: $ F = H_c + J - K $
let m_F =
Mat.add m_Hc (Mat.sub m_J m_K)
On l'exprime dans la base orthonormale, on le diagonalise, et on le re-exprime ses vecteurs propres dans la base d'OA.
let m_C =
let m_C', _ =
Util.xt_o_x m_F m_X
|> Util.diagonalize_symm
in
gemm m_X m_C'
On calcule l'energie avec ces nouvelles orbitales:
let energy =
(Simulation.nuclear_repulsion simulation) +. 0.5 *.
Mat.gemm_trace m_P (Mat.add m_Hc m_F)
On itere:
let rec iteration m_C n =
let m_P =
(* P = 2 C.C^t *)
gemm ~alpha:2. ~transb:`T ~k:nocc m_C m_C
in
let m_Hc, m_J, m_K =
let f =
Fock.make_rhf ~density:m_P ao_basis
in
Fock.(core f, coulomb f, exchange f)
in
let m_F =
Mat.add m_Hc (Mat.sub m_J m_K)
in
let m_C =
let m_C', _ =
Util.xt_o_x m_F m_X
|> Util.diagonalize_symm
in
gemm m_X m_C'
in
let energy =
(Simulation.nuclear_repulsion simulation) +. 0.5 *.
Mat.gemm_trace m_P (Mat.add m_Hc m_F)
in
Printf.printf "%f\n%!" energy;
if n > 0 then
iteration m_C (n-1)
let () = iteration m_C 20
Localisation des orbitales¶
Rotation des orbitales¶
On peut écrire une matrice de rotation $R$ comme $$ R = \exp(X) $$ où X est une matrice réelle anti-symétrique ($X_{ij} = -X_{ji}$) où $X_{ij}$ est la valeurs de l'angle de la rotation entre $i$ et $j$. Par exemple: $$ R = \left( \begin{array}{cc} \cos \theta & -\sin \theta \\ \sin \theta & \cos \theta \end{array} \right) = \exp \left( \begin{array}{cc} 0 & \theta \
- \theta & 0 \end{array} \right) $$
D'abord on diagonalise $X^2$ : $$ X^2 = W(-\tau^2) W^\dagger. $$ Ensuite, $R$ peut être écrit comme $$ R = W \cos(\tau) W^\dagger + W \tau^{-1} \sin (\tau) W^\dagger X $$
Construction de la matrice de rotation R de taille n par n:¶
Ainsi la construction d'une matrice de rotaion de taille n par n peut se faire de la manière suivante :
On part de $X$ ( n * n ) une matrice antisymétrique avec les éléments diagonaux nuls et où le terme $X_{ij}$ correspond à la valeur de l'angle de rotation i et j (dans notre cas entre l'orbitale i et j).
Tout d'abord on met au carré cette matrice X ( gemm X X ) tel que :
$$ X^2 = X X $$Ensuite on la diagonalise la matrice X² ( syev X²). Après la diagonalidsation on obtient une matrice contenant les vecteurs propres ( W ) et un vecteur contenant les différentes valeurs propres. Ces valeurs propres correspondent aux valeurs propres de X² et sont négatives, pour obtenir celle correpondant à X et qui sont positives on doit appliquer la fonction valeur absolue au vecteur et ensuite prendre sa racine carré. On obtient donc un nouveau vecteur contenant les valeurs propres de X. Il faut ensuite transformer ce vecteur en une matrice diagonale, pour cela on construit une matrice (tau) n par n où les éléments extra diagonaux sont nulles et les éléments diagonaux prennent les différentes valeurs du vecteur, tel que l'élément 1 du vecteur devient l'élément {1,1} de la matrice.
Enfin on peut calculer R comme : $$ R = W \cos(\tau) W^\dagger + W \tau^{-1} \sin (\tau) W^\dagger X $$
(* Construction de la matrice de rotation R de taille n par n *)
Principe de la rotation par paires d'orbitales.¶
La rotation des orbitales se fait de la manière suivante :
On extrait de la matrice des OMs Phi les orbitales i et j (vecteurs colonnes) pour former une nouvelle matrice Ksi de dimension n * 2.
Pour effectuer la rotation des orbitales i et j on utilise la matrice de rotation R pour la rotation de 2 orbitales, qui est définie comme la matrice :
R = ( cos(alpha) -sin(alpha) ) ( sin(alpha) cos(alpha) )
On applique R à Ksi : R Ksi = Ksi~
On obtient Ksi~ la matrice contenant les deux nouvelles OMs i~ et j~ obtenues par rotation de i et j.
On réinjecte ces deux nouvelles orbitales i~ et j~ à la place des anciennes orbitales i et j dans la matrice des OMs Phi, ce qui nous donne une nouvelle matrice des OMs Phi~. Pour cela on créer des matrices intérmédiaires:
- une matrice Psi (n par n) avec tous ses éléments nuls sauf les colonnes qui contiennent les OMs i et j
- une matrice Psi~ (n par n) avec tous ses éléments nuls sauf les colonnes qui contiennent les OMs i~ et j~ Ainsi, on soustrait à la matrice des OMs Phi la matrice Psi pour supprimer les OMs i et j de celle ci puis on additionne cette nouvelle matrice à la matrice Psi~ pour créer la nouvelle matrice des OMs Phi~ avec i~ et j~.
On repart de cette nouvelle matrice Phi~ et on cherche la paire d'orbitale (k,l) ayant le plus grand angle de rotation alpha. Et on procède comme nous l'avons précedemment de manière intérative. Le but étant de maximiser D pour la localisation de Edminston et de minimiser B pour la localisation de Boyls.
Localisation de Boys¶
$N$ orthonormal molecular orbitals $$ \phi_i({\bf r})= \sum_{k=1}^N c_{ik} \chi_k $$ $$ {\cal B}_{2|4} = \sum_{i=1}^N \langle \phi_i | ( {\bf \vec{r}} - \langle \phi_i| {\bf \vec{r}} | \phi_i …\rangle)^{2|4} | \phi_i \rangle $$ $$ {\cal B}_2= \sum_{i=1}^N \big[ \langle x^2 \rangle_i - \langle x \rangle^2_i + \langle y^2 \rangle_i - …\langle y \rangle^2_i + \langle z^2 \rangle_i - \langle z \rangle^2_i \big] $$ $$ {\cal B}2 = \sum{i=1}^N \big[ \langle x^4 \rangle_i - 4 \langle x^3 \rangle_i \langle x \rangle_i
- 6 \langle x^2 \rangle_i \langle x \rangle^2_i
- 3 \langle x \rangle^4_i \big] + \big[ ...y...] + \big[ ...z...] $$ Minimization of ${\cal B}$ with respect to an arbitrary rotation $R$ $$ \langle R \phi_i x^n R \phii \rangle = \sum{k,l=1}^N R{ik} R{il} \langle \phi_k| x^n | \phil …\rangle= $$ $$ \sum{k,l=1}^N R{ik} R{il} \sum{m,o=1}^N c{km} c_{ln} \langle \chi_m | x^n |\chio \rangle $$ We need to compute $$ S^x{n;mo}= \langle \chi_m | x^n |\chi_o \rangle $$
Rotation de deux orbitales (Boys)¶
On part de la matrice des OMs donné par $HF$ -> mC.On calcul $A{ij}^{x,y,z}$, et $B_{ij}^{x,y,z}$, tel que :
$A_{ij}^x = \langle \phi_i | x^2 | \phi_j \rangle$
$= \sum_a \sum_b c_{ai} c_{bj} \langle \chi_a | x^2 | \chi_b \rangle$
$B_{ij}^x = \langle \phi_i | x | \phi_j \rangle$
$= \sum_a \sum_b c_{ai} c_{bj} \langle \chi_a | x | \chi_b \rangle$
On forme ainsi des matrices (n par n) $A_{ij}^x$, $A_{ij}^y$, $A_{ij}^z$, $B_{ij}^x$, $B_{ij}^y$ et $B_{ij}^z$ que l'on peut sommer pour obtenir :
$A_{ij} =A_{ij}^x + A_{ij}^y + A_{ij}^z$ et $B_{ij} =B_{ij}^x + B_{ij}^y + B_{ij}^z$
$D$ est défini comme :
$D(\theta) = D(0) + \frac{1}{4} [(1-cos4\theta)\beta_{ij}+sin4\theta \gamma_{ij}] $
Où
$\beta_{ij}= (B^x_{ii}-B^x_{jj})^2 - 4 {(B^x_{ij})}^2 + [...y...] + [...z...]$
$=(B_{ii}-B_{jj})^2 - 4 {(B_{ij})}^2 $
Et
$\gamma_{ij}= 4 B^x_{ij} (B^{x}_{ii}-B^x_{jj}) + [...y...] + [...z...]$
$=4 B_{ij} (B_{ii}-B_{jj})$
Avec
$D(0)= D^{x}(0) + D^{y}(0) + D^{z}(0)$
$=A_{ii}^x + A_{jj}^x - (\tilde B_{ii}^x)^2 - (\tilde B_{jj}^x)^2$ $+A_{ii}^y + A_{jj}^y - (\tilde B_{ii}^y)^2 - (\tilde B_{jj}^y)^2$ $+A_{ii}^z + A_{jj}^z - (\tilde B_{ii}^z)^2 - (\tilde B_{jj}^z)^2$
$= A_{ii} + A_{jj} - \tilde B_{ii}^2 - \tilde B_{jj}^2$
Avec
${\tilde B}^{x2}_{ii}= (cos^2\theta B^x_{ii} + sin^2\theta B^x_{jj} - sin2\theta B^x_{ij})^2$
${\tilde B}^{x2}_{jj}= (sin^2\theta B^x_{ii} + cos^2\theta B^x_{jj} + sin2\theta B^x_{ij})^2$
Et donc :
${\tilde B}^{2}_{ii}= (cos^2\theta B_{ii} + sin^2\theta B_{jj} - sin2\theta B_{ij})^2$
${\tilde B}^{2}_{jj}= (sin^2\theta B_{ii} + cos^2\theta B_{jj} + sin2\theta B_{ij})^2$
Ainsi, on a :
$D(\theta) = A_{ii} + A_{jj} - (cos^2\theta B_{ii} + sin^2\theta B_{jj} - sin2\theta B_{ij})^2 - (sin^2\theta B_{ii} + cos^2\theta B_{jj} + sin2\theta B_{ij})^2$
Les extrema de $D(\theta)$ sont de la forme :
$Tan(\theta) = - \frac{\gamma}{\beta}$
$D(\theta)$ à 4 extrema :
$4\theta; \;\; 4\theta +\pi; \;\; 4\theta+ 2\pi; \;\; 4\theta+ 3\pi$
$\theta_{ij}= Atan(- \frac{\gamma}{\beta})$
Ainsi on peut calculer la matrice des $\theta$ et effectuer la rotation pour le couple d'orbitale $(i,j)$ ayant le $\theta_{max}$
Sinon on procède comme Edminson Ruedenberg mais avec les intégrales $A_{12}$ et $B_{12}$ définies comme :
$A^r_{12} = \langle \phi_1 | \bar r | \phi_2 \rangle $ $*\langle \phi_1 | \bar r | \phi_2 \rangle $ $- \frac {1}{4}(\langle \phi_1 | \bar r | \phi_1 \rangle $ $- \langle \phi_2 | \bar r | \phi_2 \rangle * \langle \phi_1 | \bar r | \phi_1 \rangle$ $- \langle \phi_2 | \bar r | \phi_2 \rangle)$
Et
$B^r_{12} = (\langle \phi_1 | \bar r | \phi_1 \rangle - \langle \phi_2 | \bar r | \phi_2 \rangle)$ $ * \langle \phi_1 | \bar r | \phi_2 \rangle $
Avec
$A^r_{12}=A^x_{12} + A^y_{12} + A^z_{12}$
$B^r_{12}=B^x_{12} + B^y_{12} + B^z_{12}$
Et le critère à minimiser est :
$D= \sum_i < \phi_i | r | \phi_i >$
Avec
$< \phi_i | r | \phi_i > = < \phi_i | x | \phi_i > + < \phi_i | y | \phi_i > + < \phi_i | z | \phi_i >$
Rotation of angle $\theta$ $$ \tilde{\phi}_1 = cos\theta \phi_1 -sin\theta \phi_2 $$ $$ \tilde{\phi}_2 = sin\theta \phi_1 +cos\theta \phi_2 $$ Let us note $$ {\cal B}_x(\theta) = \langle x^2 \rangle_{\tilde{1}} - \langle x \rangle^2_{\tilde{1}} + \langle x^2 \rangle_{\tilde{2}} - \langle x \rangle^2_{\tilde{2}} $$ and \begin{eqnarray} A^x_{ij} & = & \langle \phi_i| x^2 | \phi_j \rangle \\ B^x_{ij} & = & \langle \phi_i| x | \phi_j \rangle \;\; i,j=1,2 \end{eqnarray} We have $$ {\cal B}_x(\theta) = A^x_{11}+A^x_{22} - ({\tilde B}^{x2}_{11} + {\tilde B}^{x2}_{22}) $$ with $$ {\tilde B}^{x2}_{11}= (cos^2\theta B^x_{11} + sin^2\theta B^x_{22} - sin2\theta B^x_{12})^2 $$ $$ {\tilde B}^{x2}_{22}= (sin^2\theta B^x_{11} + cos^2\theta B^x_{22} + sin2\theta B^x_{12})^2 $$ $$ {\cal B}(\theta)= {\cal B}_x(\theta)+{\cal B}_y(\theta)+{\cal B}_z(\theta)=(x) + (y)+(z) $$ with $$ {\cal B}x(\theta)= A^x{11}+A^x_{22}
- [(cos^4\theta+ sin^4\theta)({B^x{11}}^2+ {B^x{22}}^2 )
- 2 sin^2 2\theta {B^x_{12}}^2
- 2 sin 2\theta cos 2\theta (({B^x{22}} -{B^x{11}} ) {B^x_{12}}] $$ and idem for $y$ and $z$. Using the fact that $$ cos^4\theta+ sin^4\theta= \frac{1}{4} ( 3 + cos4\theta) $$ $$ {\cal B}x(\theta)= A^x{11}+A^x_{22}
- [ \frac{1}{4} ( 3 + cos4\theta)({B^x{11}}^2+ {B^x{22}}^2 )
- (1 -cos 4\theta) {B^x_{12}}^2
- sin 4\theta (({B^x{22}} -{B^x{11}} ) {B^x{12}}] $$ Finally, we get \begin{equation} {\cal B}(\theta)= {\cal B}(0) + \frac{1}{4} [(1-cos4\theta)\beta+sin4\theta \gamma] \end{equation} where $$ {\cal B}(0)= A^x{11}+A^x{22} -((B^{x}{11})^2+(B^{x}{22})^2) + [...y...] + [...z...] $$ $$ \beta= (B^x{11}-B^x{22})^2 - 4 {(B^x{12})}^2 + [...y...] + [...z...] $$ and $$ \gamma= 4 B^x{12} (B^{x}{11}-B^x_{22}) + [...y...] + [...z...] $$ Let us compute the derivative; we get $$ \frac{\partial {\cal B}(\theta)}{\partial \theta} = \beta sin4\theta
- \gamma cos4\theta $$ Extrema of ${\cal B}(\theta)$ \begin{equation} tg4\theta= -\frac{\gamma}{\beta} \end{equation} There are four extrema: $$ 4\theta; \;\; 4\theta +\pi; \;\; 4\theta+ 2\pi; \;\; 4\theta+ 3\pi $$ Value of the second derivative of $\cal{B}$ at the extrema Value of $\cal{B}$ at the extrema \begin{equation} \frac{\partial^2 B(\theta)}{\partial \theta^2}= 4 cos4\theta \frac{\beta^2 + \gamma^2}{\beta} \end{equation} There are two minima and two maxima since $cos4\theta= -cos4(\theta+\pi)= -cos4(\theta+2\pi)=-cos4(\theta+3\pi)$. Value of $\cal{B}$ at the extrema \begin{equation} {\cal B}(\theta)= {\cal B}(0) + \frac{1}{4} (\beta -\frac{\beta^2 + \gamma^2}{\beta} {cos4\theta}) \end{equation}
Localisation de Edmiston, C., & Ruedenberg, K.¶
(Localisation des orbitales par rotation de paires d'orbitales)
Ref : Edmiston, C., & Ruedenberg, K. (1963). Localized Atomic and Molecular Orbitals. Reviews of Modern Physics, 35(3), 457–464. doi:10.1103/revmodphys.35.457
Méthode :
Le but de cette méthode est de maximiser D :
$D(phi)= \sum_n [Phi_n^2 | Phi_n^2 ]$
$= \sum_n < Phi²_n | 1/r_{12} | Phi²_n >$
Car selon J. E. Lennard-Jones and J. A. Pople, Proc. Roy. Soc. (London) A202, 166 (1950), on peut générer des orbitales équivalentes et celles ci maximiseront probablement la somme des termes d'auto répulsion orbitalaire D.
On va créer des nouvelles orbitales i~ et j~ à partir des orbitales i et j par combinaison linéaire de ces dernières tel que :
$i~ (x) = cos(\gamma) i(x) + sin(\gamma) j(x)$
$j~ (x) = -sin(\gamma) i(x) + cos(\gamma) j(x)$
On part de la matrices de orbitales moléculaires Phi et on cherche la paire d'orbitale (i,j) ayant le plus grand angle de rotation alpha, avec alpha défini comme (si ce dernier est supétieur à $\frac{\pi}{2}$ on devra soustraire $\frac{\pi}{2}$ aux éléments de la matrices :
$Cos(4 \alpha)= -A_{12} / (A_{12}^2 + B_{12}^2)^{1/2}$
$\alpha = (1/4) Acos (-A_{12} / (A_{12}^2 + B_{12}^2)^{1/2})$
$Sin(4 \alpha)= B_{12} / (A_{12}^2 + B_{12}^2)^{1/2}$
$\alpha = (1/4) Asin (B_{12} / (A_{12}^2 + B_{12}^2)^{1/2})$
$Tan(4 \alpha) = -B_{12} / A_{12}$
$\alpha = (1/4) Atan (-B_{12} / A_{12})$
Avec :
$A_{ij} = [ \phi_i \phi_j | \phi_i \phi_j] - 1/4 [\phi_i^2 - \phi_j^2 | \phi_i^2 - \phi_j^2] $
Où :
$ [ \phi_i \phi_j | \phi_i \phi_j] = \sum_a c_{ai} \langle \chi_a \phi_j | \phi_i \phi_j \rangle$
$=\sum_a \sum_b c_{ai} c_{bj} \langle \chi_a \chi_b | \phi_i \phi_j \rangle $
$=\left(\sum_a c_{ai} \left(\sum_b c_{bj} \left(\sum_e c_{ei} \left(\sum_f c_{fj} \langle \chi_a \chi_b | \chi_e \chi_f \rangle \right)\right)\right)\right) $
$=\sum_a \sum_b \sum_e \sum_f \left( c_{ai} c_{bj} c_{ei} c_{fj} \langle \chi_a \chi_b | \chi_e \chi_f \rangle \right) $
Et :
$\phi_i ^2 = \left( \sum_a c_{ai} \chi_a \right)^2$ $= \sum_a c_{ai} \sum_b c_{bi} \chi_a \chi_b$
$\phi_i ^2 - \phi_j ^2 = \left( \sum_a c_{ai} \chi_a \right)^2 - \left( \sum_a c_{aj} \chi_a \right)^2$ $= \sum_a c_{ai} \sum_b c_{bi} \chi_a \chi_b - \sum_a c_{aj} \sum_b c_{bj} \chi_a \chi_b$
$[\phi_i^2 -\phi_j^2 |\phi_i^2 -\phi_j^2] = [\left( \sum_a c_{ai} \chi_i \right)^2 - \left( \sum_a c_{aj} \chi_j \right)^2|\phi_i^2 -\phi_j^2]$
$= \sum_a c_{ai} \sum_b c_{bi} [\chi_a \chi_b| \phi_i^2 -\phi_j^2 ] - \sum_a c_{aj} \sum_b c_{bj} [ \chi_a \chi_b| \phi_i^2 -\phi_j^2 ] $
$= \left(\sum_a c_{ai} \sum_b c_{bi} - \sum_a c_{aj} \sum_b c_{bj} \right) [ \chi_a \chi_b| \phi_i^2 -\phi_j^2 ] $
$= \left(\sum_a c_{ai} \sum_b c_{bi} - \sum_a c_{aj} \sum_b c_{bj} \right) [ \chi_a \chi_b| \left( \sum_a c_{ai} \chi_i \right)^2 - \left( \sum_a c_{aj} \chi_j \right)^2 ] $
$= \left(\sum_e c_{ei} \sum_f c_{fi} - \sum_e c_{ej} \sum_f c_{fj} \right) \left(\sum_a c_{ai} \sum_b c_{bi} - \sum_a c_{aj} \sum_b c_{bj} \right) [ \chi_a \chi_b| \chi_e \chi_f ] $
Mais aussi :
$B_{ij} = [\phi_i ^2 - \phi_j ^2 | \phi_i \phi_j ] = [\left( \sum_a c_{ai} \chi_i \right)^2 - \left( \sum_a c_{aj} \chi_j \right)^2| \phi_i \phi_j ] $
$= \sum_a c_{ai} \sum_b c_{bi} [\chi_a \chi_b| \phi_i \phi_j ] - \sum_a c_{aj} \sum_b c_{bj} [ \chi_a \chi_b| \phi_i \phi_j ] $
$= \left(\sum_a c_{ai} \sum_b c_{bi} - \sum_a c_{aj} \sum_b c_{bj} \right) [ \chi_a \chi_b| \phi_i \phi_j ] $
$= \left(\sum_a c_{ai} \sum_b c_{bi} - \sum_a c_{aj} \sum_b c_{bj} \right) \sum_e \sum_f c_{ei} c_{fj} [ \chi_a \chi_b| \chi_e \chi_f ] $
On extrait de la matrice des OMs les orbitales i et j (vecteurs colonnes) pour former une nouvelle matrice m_Ksi de dimension n * 2.
Pour effectuer la rotation des orbitales i et j on utilise la matrice de rotation m_R pour la rotation de 2 orbitales, qui est définie comme :
m_R =
( cos(alpha) -sin(alpha) )
( sin(alpha) cos(alpha) )
On applique m_R à m_Ksi : m_R m_Ksi = m_Ksi_thilde
On obtient m_Ksi_tilde la matrice contenant les deux nouvelles OMs i~ et j~ obtenues par rotation de i et j.
On réinjecte ces deux nouvelles orbitales i~ et j~ à la place des anciennes orbitales i et j dans la matrice des OMs m_Phi, ce qui nous donne une nouvelle matrice des OMs m_Phi_tilde. Pour cela on créer des matrices intérmédiaires:
- une matrice ( m_Psi ) n par n avec tous ses éléments nuls sauf les colonnes qui contiennent les OMs i et j
- une matrice ( m_Psi_tilde ) n par n avec tous ses éléments nuls sauf les colonnes qui contiennent les OMs i~ et j~
- une matrice ( m_interm ) n par n où l'on a soustrait m_Psi à m_Phi pour créer des 0 sur les colonnes des OMs i et j
Ainsi, on soustrait à la matrice des OMs Phi la matrice m_ksi pour supprimer les OMs i et j de celle ci puis on additionne cette nouvelle matrice à la matrice m_ksi_tilde pour créer la nouvelle matrice des OMs m_Phi_tilde.
On repart de cette nouvelle matrice m_Phi_tilde et on cherche la paire d'orbitale (k,l) ayant le plus grand angle de rotation alpha. Et on procède comme nous l'avons précedemment de manière intérative. Le but étant de maximiser D, c'est à dire maximiser le terme de répulsion.
Pour le calcul de D (le critère de localisation qu'on maximise) , sachant que :
$\phi_i ^2 = \left( \sum_a c_{ai} \chi_a \right)^2$ $= \sum_a c_{ai} \sum_b c_{bi} \chi_a \chi_b$
On aura :
$D=\sum_n [\phi_n^2|\phi_n^2]$
$= \sum_n (\sum_a c_{an} \sum_b c_{bn} [\chi_a \chi_b| \phi_n^2])$
$= \sum_n (\sum_a c_{an} \sum_b c_{bn} \sum_e c_{en} \sum_f c_{fn} [\chi_a \chi_b| \chi_e \chi_f])$
$= \sum_n (\sum_a \sum_b \sum_e \sum_f c_{an} c_{bn} c_{en} c_{fn} [\chi_a \chi_b| \chi_e \chi_f])$
Calculs : Localisation de Edminston¶
ER-V1¶
-> Fonctionne, il n'itère pas => Point de départ
(*
(* Localisation de Edminstion *)
let ee_ints = AOBasis.ee_ints ao_basis;;
let m_C = MOBasis.mo_coef mo_basis;;
let n_ao = Mat.dim1 m_C ;;
let n_mo = Mat.dim2 m_C ;;
let sum a =
Array.fold_left (fun accu x -> accu +. x) 0. a
;;
(*Calcul des intégrales*)
let integral_general g i j =
Array.map (fun a ->
let v =
Array.map (fun b ->
let u =
Array.map (fun e ->
let t = Array.map (fun f ->
(g a b e f i j) *. ERI.get_phys ee_ints a e b f
) (Util.array_range 1 n_ao)
in sum t
) (Util.array_range 1 n_ao)
in sum u
) (Util.array_range 1 n_ao)
in sum v
) (Util.array_range 1 n_ao)
|> sum
;;
(* Calcul de toutes les intégrales B_12 -> Matrice *)
let m_b12 = Mat.init_cols n_ao n_ao (fun i j ->
integral_general (fun a b e f i j ->
( m_C.{a,i} *. m_C.{b,i} -. m_C.{a,j} *. m_C.{b,j} ) *. m_C.{e,i} *. m_C.{f,j}
) i j
)
(* Calcul de toutes les intégrales A_12 -> Matrice *)
let m_a12 = Mat.init_cols n_mo n_mo (fun i j ->
integral_general (fun a b e f i j -> m_C.{a,i} *. m_C.{b,j} *. m_C.{e,i} *. m_C.{f,j}
-. 0.25 *. ( m_C.{e,i} *. m_C.{f,i} -. m_C.{e,j} *. m_C.{f,j} )
*. ( m_C.{a,i} *. m_C.{b,i} -. m_C.{a,j} *. m_C.{b,j} )
) i j
)
(* Calcul de tous les alpha -> Matrice *)
let m_alpha = Mat.init_cols n_mo n_mo ( fun i j -> if i= j
then 0.
else 0.25 *. (acos(-. m_a12.{i,j} /. sqrt((m_a12.{i,j}**2.) +. (m_b12.{i,j}**2.)))))
(*
let s_D =
let v_D =
let m_D = Mat.init_cols n_mo n_mo (fun i j ->
integral_general (fun a b e f i j -> m_C.{a,i} *. m_C.{b,i} *. m_C.{e,i} *. m_C.{f,i}
) i j
)
in Vec.init n_mo ( fun i -> m_D.{i,1} )
in Vec.sum v_D
*)
(* Calcul de D *)
let m_D = Mat.init_cols n_mo n_mo (fun i j ->
integral_general (fun a b e f i j -> m_C.{a,i} *. m_C.{b,i} *. m_C.{e,i} *. m_C.{f,i}
) i j
)
let v_D = Vec.init n_mo ( fun i -> m_D.{i,1} )
let s_D = Vec.sum v_D
(*
(* Calcul de D *)
let v_D = Vec.init n_ao (fun i ->
integral_general (fun a b e f i _ -> m_C.{a,i} *. m_C.{b,i} *. m_C.{e,i} *. m_C.{f,i}
)
)
let s_D = Vec.sum v_D
*)
(* Rotation des orbitales *)
(* Extraction du plus grand angle alpha*)
let ij_max_alpha = Mat.as_vec m_alpha
|> iamax;;
(* Indices du plus grand angle alpha *)
let indice_i = (ij_max_alpha - 1) mod n_ao + 1;;
let indice_j = (ij_max_alpha - 1) / n_mo + 1;;
(* Valeur du plus grand angle alpha*)
let alpha = m_alpha.{indice_i,indice_j};;
let m_Phi = m_C
(* Matrice de rotation 2 par 2 *)
let m_R = Mat.init_cols 2 2 (fun i j -> if i=j then cos alpha
else if i>j then sin alpha
else -. sin alpha );;
(* Fonction d'extraction des 2 vecteurs propres i et j de la matrice des OMs pour les mettres dans la matrice Ksi (n par 2)
pour appliquer R afin d'effectuer la rotation des orbitales *) (* {1,2} -> 1ere ligne, 2e colonne *)
let p = indice_i;;
let q = indice_j;;
let m_Ksi = Mat.init_cols n_ao 2 (fun i j -> if j=1 then m_Phi.{i,p} else m_Phi.{i,q} );;
(* Fonction de calcul de ksi~ (matrice n par 2), nouvelle matrice par application de la matrice de rotation dans laquelle on
obtient les deux orbitales que l'on va réinjecter dans la matrice Phi*)
let m_Ksi_tilde = gemm m_Ksi m_R;;
(* Réinjection*)
(* Pour la réinjection on créer des matrices intérmédiares, une matrice nulle partout sauf sur
les colonnes de i et j et de i~ et j~. On fait la différence de la première matrice avec la matrice
des OMs Phi afin de substituer les colonnes de i et j par des zéro et ensuite sommer cette matrice avec
celle contenant i~ et j~ *)
(* Matrice intérmédiare pour l'injection de ksi~ (i~ et j~) dans la matrice Phi *)
let m_Psi_tilde = Mat.init_cols n_ao n_ao (fun i j -> if j=q then m_Ksi_tilde.{i,2}
else if j=p then m_Ksi_tilde.{i,1}
else 0.);;
(* p q verif*)
(* Matrice intermédiaire pour supprimer ksi (i et j) dans la matrice Phi *)
let m_Psi = Mat.init_cols n_ao n_ao (fun i j -> if j=q then m_Ksi.{i,2}
else if j=p then m_Ksi.{i,1}
else 0.);;
(* Matrice intérmédiaire où les orbitales i et j ont été supprimées et remplacées par des 0, par soustraction de la matrice Phi
par la matrice *)
let m_interm = Mat.sub m_Phi m_Psi;;
(* Construction de la nouvelle matrice d'OMs phi~ *)
let m_Phi_tilde = Mat.add m_Psi_tilde m_interm;;
*)
TESTS¶
Réécriture pour itérerer ER¶
Réécriture des fonctions avec leurs arguments¶
-> pour refaire mieux la boucle en évitant des problèmes
Fonctions¶
(* Définitions de base nécessaire pour la suite *)
let ee_ints = AOBasis.ee_ints ao_basis;;
let m_C = MOBasis.mo_coef mo_basis;;
let n_ao = Mat.dim1 m_C ;;
let n_mo = Mat.dim2 m_C ;;
let multipoles =
AOBasis.multipole ao_basis;;
let sum a =
Array.fold_left (fun accu x -> accu +. x) 0. a
type alphaij = {
alpha_max : float;
indice_ii : int;
indice_jj : int;};;
(* Fonction de calcul de tous les alpha ER -> Matrice, dépend de m_a12, m_b12 qui dépendent de m_C *)
let f_alpha m_C =
let n_mo = Mat.dim2 m_C in
let t0 = Sys.time () in
let m_b12 = Mat.init_cols n_mo n_mo (fun i j -> 0.) in
let m_a12 = Mat.init_cols n_mo n_mo (fun i j -> 0.) in
(* Tableaux temporaires *)
let m_pqr =
Bigarray.(Array3.create Float64 fortran_layout n_ao n_ao n_ao)
in
let m_qr_i = Mat.create (n_ao*n_ao) n_mo in
let m_ri_j = Mat.create (n_ao*n_mo) n_mo in
let m_ij_k = Mat.create (n_mo*n_mo) n_mo in
Array.iter (fun s ->
(* Grosse boucle externe sur s *)
Array.iter (fun r ->
Array.iter (fun q ->
Array.iter (fun p ->
m_pqr.{p,q,r} <- ERI.get_phys ee_ints p q r s
) (Util.array_range 1 n_ao)
) (Util.array_range 1 n_ao)
) (Util.array_range 1 n_ao);
(* Conversion d'un tableau a 3 indices en une matrice nao x nao^2 *)
let m_p_qr =
Bigarray.reshape (Bigarray.genarray_of_array3 m_pqr) [| n_ao ; n_ao*n_ao |]
|> Bigarray.array2_of_genarray
in
let m_qr_i =
(* (qr,i) = <i r|q s> = \sum_p <p r | q s> C_{pi} *)
gemm ~transa:`T ~c:m_qr_i m_p_qr m_C
in
let m_q_ri =
(* Transformation de la matrice (qr,i) en (q,ri) *)
Bigarray.reshape_2 (Bigarray.genarray_of_array2 m_qr_i) n_ao (n_ao*n_mo)
in
let m_ri_j =
(* (ri,j) = <i r | j s> = \sum_q <i r | q s> C_{bj} *)
gemm ~transa:`T ~c:m_ri_j m_q_ri m_C
in
let m_r_ij =
(* Transformation de la matrice (ri,j) en (r,ij) *)
Bigarray.reshape_2 (Bigarray.genarray_of_array2 m_ri_j) n_ao (n_mo*n_mo)
in
let m_ij_k =
(* (ij,k) = <i k | j s> = \sum_r <i r | j s> C_{rk} *)
gemm ~transa:`T ~c:m_ij_k m_r_ij m_C
in
let m_ijk =
(* Transformation de la matrice (ei,j) en (e,ij) *)
Bigarray.reshape (Bigarray.genarray_of_array2 m_ij_k) [| n_mo ; n_mo ; n_mo |]
|> Bigarray.array3_of_genarray
in
Array.iter (fun j ->
Array.iter (fun i ->
m_b12.{i,j} <- m_b12.{i,j} +. m_C.{s,j} *. (m_ijk.{i,i,i} -. m_ijk.{j,i,j});
m_a12.{i,j} <- m_a12.{i,j} +. m_ijk.{i,i,j} *. m_C.{s,j} -.
0.25 *. ( (m_ijk.{i,i,i} -. m_ijk.{j,i,j}) *. m_C.{s,i} +.
(m_ijk.{j,j,j} -. m_ijk.{i,j,i}) *. m_C.{s,j})
) (Util.array_range 1 n_mo)
) (Util.array_range 1 n_mo)
) (Util.array_range 1 n_ao);
let t1 = Sys.time () in
Printf.printf "t = %f s\n%!" (t1 -. t0);
Mat.init_cols n_mo n_mo ( fun i j ->
if i= j then 0.
else 0.25 *. (acos(-. m_a12.{i,j} /. sqrt((m_a12.{i,j}**2.) +. (m_b12.{i,j}**2. ))))
);;
(*********************)
f_alpha m_C;;
(*Fonction général de calcul des intégrales*)
let integral_general g i j =
Array.map (fun a ->
let v =
Array.map (fun b ->
let u =
Array.map (fun e ->
let t = Array.map (fun f ->
(g a b e f i j) *. ERI.get_phys ee_ints a e b f
) (Util.array_range 1 n_ao)
in sum t
) (Util.array_range 1 n_ao)
in sum u
) (Util.array_range 1 n_ao)
in sum v
) (Util.array_range 1 n_ao)
|> sum
(* Fonction de calcul de tous les alpha ER -> Matrice, dépend de m_a12, m_b12 qui dépendent de m_C *)
let f_alpha m_C =
let n_mo = Mat.dim2 m_C
in
(* Fonction de calcul de toutes les intégrales B_12 -> Matrice, dépend de m_C *)
let m_b12 = Mat.init_cols n_mo n_mo (fun i j ->
integral_general (fun a b e f i j ->
( m_C.{a,i} *. m_C.{b,i} -. m_C.{a,j} *. m_C.{b,j} ) *. m_C.{e,i} *. m_C.{f,j}
) i j
)
in
(* Fonction de calcul de toutes les intégrales A_12 -> Matrice, dépend de m_C *)
let m_a12 = Mat.init_cols n_mo n_mo (fun i j ->
integral_general (fun a b e f i j -> m_C.{a,i} *. m_C.{b,j} *. m_C.{e,i} *. m_C.{f,j}
-. 0.25 *. (( m_C.{e,i} *. m_C.{f,i} -. m_C.{e,j} *. m_C.{f,j} )
*. ( m_C.{a,i} *. m_C.{b,i} -. m_C.{a,j} *. m_C.{b,j} ))
) i j
)
in
Mat.init_cols n_mo n_mo ( fun i j ->
if i= j
then 0.
else 0.25 *. (acos(-. m_a12.{i,j} /. sqrt((m_a12.{i,j}**2.) +. (m_b12.{i,j}**2.)))));;
(*********************)
f_alpha m_C;;
(* Fonction de calcul de tous les alpha ER -> Matrice, dépend de m_a12, m_b12 qui dépendent de m_C *)
let f_alpha m_C =
let n_mo = Mat.dim2 m_C in
let t0 = Sys.time () in
let m_b12 = Mat.init_cols n_mo n_mo (fun i j -> 0.) in
let m_a12 = Mat.init_cols n_mo n_mo (fun i j -> 0.) in
let m_d = Mat.init_cols n_mo n_mo (fun i j -> 0.) in
(* Tableaux temporaires *)
let m_pqr =
Bigarray.(Array3.create Float64 fortran_layout n_ao n_ao n_ao)
in
let m_qr_i = Mat.create (n_ao*n_ao) n_mo in
let m_ri_j = Mat.create (n_ao*n_mo) n_mo in
let m_ij_k = Mat.create (n_mo*n_mo) n_mo in
Array.iter (fun s ->
(* Grosse boucle externe sur s *)
Array.iter (fun r ->
Array.iter (fun q ->
Array.iter (fun p ->
m_pqr.{p,q,r} <- ERI.get_phys ee_ints p q r s
) (Util.array_range 1 n_ao)
) (Util.array_range 1 n_ao)
) (Util.array_range 1 n_ao);
(* Conversion d'un tableau a 3 indices en une matrice nao x nao^2 *)
let m_p_qr =
Bigarray.reshape (Bigarray.genarray_of_array3 m_pqr) [| n_ao ; n_ao*n_ao |]
|> Bigarray.array2_of_genarray
in
let m_qr_i =
(* (qr,i) = <i r|q s> = \sum_p <p r | q s> C_{pi} *)
gemm ~transa:`T ~c:m_qr_i m_p_qr m_C
in
let m_q_ri =
(* Transformation de la matrice (qr,i) en (q,ri) *)
Bigarray.reshape_2 (Bigarray.genarray_of_array2 m_qr_i) n_ao (n_ao*n_mo)
in
let m_ri_j =
(* (ri,j) = <i r | j s> = \sum_q <i r | q s> C_{bj} *)
gemm ~transa:`T ~c:m_ri_j m_q_ri m_C
in
let m_r_ij =
(* Transformation de la matrice (ri,j) en (r,ij) *)
Bigarray.reshape_2 (Bigarray.genarray_of_array2 m_ri_j) n_ao (n_mo*n_mo)
in
let m_ij_k =
(* (ij,k) = <i k | j s> = \sum_r <i r | j s> C_{rk} *)
gemm ~transa:`T ~c:m_ij_k m_r_ij m_C
in
let m_ijk =
(* Transformation de la matrice (ei,j) en (e,ij) *)
Bigarray.reshape (Bigarray.genarray_of_array2 m_ij_k) [| n_mo ; n_mo ; n_mo |]
|> Bigarray.array3_of_genarray
in
Array.iter (fun j ->
Array.iter (fun i ->
Array.iter (fun i ->
m_b12.{i,j} <- m_b12.{i,j} +. m_C.{s,j} *. (m_ijk.{i,i,i} -. m_ijk.{j,i,j});
m_a12.{i,j} <- m_a12.{i,j} +. m_ijk.{i,i,j} *. m_C.{s,j} -.
0.25 *. ( (m_ijk.{i,i,i} -. m_ijk.{j,i,j}) *. m_C.{s,i} +.
(m_ijk.{j,j,j} -. m_ijk.{i,j,i}) *. m_C.{s,j});
m_d.{i,j} <- m_d.{i,j} +. m_ijk.{i,i,j} *. m_C.{s,j}
) (Util.array_range 1 n_mo)
) (Util.array_range 1 n_mo)
) (Util.array_range 1 n_mo)
) (Util.array_range 1 n_ao);
let t1 = Sys.time () in
Printf.printf "t = %f s\n%!" (t1 -. t0);
(Mat.init_cols n_mo n_mo ( fun i j ->
if i= j then 0.
else 0.25 *. (acos(-. m_a12.{i,j} /. sqrt((m_a12.{i,j}**2.) +. (m_b12.{i,j}**2. ))))
),Mat.init_cols n_mo n_mo ( fun i j -> m_d.{i,j}));;
(*********************)
f_alpha m_C;;
let m_alpha , m_d = f_alpha m_C;;
let s_D m_C =
let v_D =
let n_mo = Mat.dim2 m_C
in Vec.init n_mo ( fun i -> m_d.{i,i} )
in Vec.sum v_D ;;
s_D m_C;;
(*
(* Fonction de calcul de tous les alpha ER -> Matrice, dépend de m_a12, m_b12 qui dépendent de m_C *)
let f_alpha m_C eps=
let n_mo = Mat.dim2 m_C
in
(* Fonction de calcul de toutes les intégrales B_12 -> Matrice, dépend de m_C *)
let m_b12 = Mat.init_cols n_mo n_mo (fun i j -> 0.) in
let m_a12 = Mat.init_cols n_mo n_mo (fun i j -> 0.) in
Array.iter (fun a ->
Array.iter (fun b ->
let mca = Vec.init n_mo (fun i -> m_C.{a,i} *. m_C.{b,i}) in
Array.iter (fun e ->
Array.iter (fun f ->
let integral = ERI.get_phys ee_ints f b e a in
if abs_float integral > eps then
Array.iter ( fun i ->
let mcei = m_C.{e,i} *. m_C.{f,i} in
Array.iter ( fun j ->
let x = m_C.{e,i} *. m_C.{f,j} *. integral in
let mcaij = ( mca.{i} -. mca.{j} ) in
m_b12.{i,j} <- m_b12.{i,j} +. mcaij *. x;
m_a12.{i,j} <- m_a12.{i,j} +. m_C.{a,i} *. m_C.{b,j} *. x
-. 0.25 *. ( mcei -. m_C.{e,j} *. m_C.{f,j} ) *. mcaij *. integral
) (Util.array_range 1 n_mo)
) (Util.array_range 1 n_mo)
) (Util.array_range 1 n_ao)
) (Util.array_range 1 n_ao)
) (Util.array_range 1 n_ao)
) (Util.array_range 1 n_ao);
Mat.init_cols n_mo n_mo ( fun i j ->
if i= j then 0.
else 0.25 *. (acos(-. m_a12.{i,j} /. sqrt((m_a12.{i,j}**2.) +. (m_b12.{i,j}**2.)))));;
(*********************)
f_alpha m_C 1.e-5;;
*)
(* Calcul de D -> critère à maximiser dans ER*)
let s_D m_C =
let v_D =
let n_mo = Mat.dim2 m_C
in
let m_D = Mat.init_cols n_mo n_mo (fun i j ->
integral_general (fun a b e f i j -> m_C.{a,i} *. m_C.{b,i} *. m_C.{e,i} *. m_C.{f,i}
) i j
)
in Vec.init n_mo ( fun i -> m_D.{i,i} )
in Vec.sum v_D ;;
(******************)
let m_D = Mat.init_cols n_mo n_mo (fun i j ->
integral_general (fun a b e f i j -> m_C.{a,i} *. m_C.{b,i} *. m_C.{e,i} *. m_C.{f,i}
) i j
);;
let toto = s_D m_C;;
toto *. 6.;;
(* Fonction calcul alpha Boys *)
let f_alpha_boys m_C =
let n_mo = Mat.dim2 m_C
in
let phi_x_phi =
Multipole.matrix_x multipoles
|> MOBasis.mo_matrix_of_ao_matrix ~mo_coef:m_C
in
let phi_y_phi =
Multipole.matrix_y multipoles
|> MOBasis.mo_matrix_of_ao_matrix ~mo_coef:m_C
in
let phi_z_phi =
Multipole.matrix_z multipoles
|> MOBasis.mo_matrix_of_ao_matrix ~mo_coef:m_C
in
let m_b12=
let b12 g = Mat.init_cols n_mo n_mo ( fun i j ->
(g.{i,i} -. g.{j,j}) *. g.{i,j})
in
Mat.add (b12 phi_x_phi) ( Mat.add (b12 phi_y_phi) (b12 phi_z_phi))
in
let m_a12 =
let a12 g = Mat.init_cols n_mo n_mo ( fun i j ->
g.{i,j} *. g.{i,j} -. 0.25 *. ((g.{i,i} -. g.{j,j}) *. (g.{i,i} -. g.{j,j})))
in
Mat.add (a12 phi_x_phi) ( Mat.add (a12 phi_y_phi) (a12 phi_z_phi))
in
Mat.init_cols n_mo n_mo ( fun i j ->
if i=j
then 0.
else 0.25 *. acos(-. m_a12.{i,j} /. sqrt((m_a12.{i,j}**2.) +. (m_b12.{i,j}**2.) )));;
(****************************)
(*
f_alpha_boys m_C;;
*)
(* Fonction de calcul de D Boys *)
let d_boys m_C =
let phi_x_phi =
Multipole.matrix_x multipoles
|> MOBasis.mo_matrix_of_ao_matrix ~mo_coef:m_C
in
(*Util.debug_matrix "phi_x_phi" phi_x_phi;*)
let phi_y_phi =
Multipole.matrix_y multipoles
|> MOBasis.mo_matrix_of_ao_matrix ~mo_coef:m_C
in
(*Util.debug_matrix "phi_y_phi" phi_y_phi;*)
let phi_z_phi =
Multipole.matrix_z multipoles
|> MOBasis.mo_matrix_of_ao_matrix ~mo_coef:m_C
in
(*Util.debug_matrix "phi_z_phi" phi_z_phi;*)
let v_D_boys =
let n_mo = Mat.dim2 m_C
in
Vec.init n_mo ( fun i -> (phi_x_phi.{i,i})**2. +. (phi_y_phi.{i,i})**2. +. (phi_z_phi.{i,i})**2.)
in
Vec.sum v_D_boys;;
(*************************)
(*Multipole.matrix_x2 multipoles;;
d_boys m_C;;*)
(* Test méthode de calcul de alpha et de D ensemble *)
type alphad = {
m_alpha : Mat.t;
d : float;
}
let m_alpha_d methode m_C =
let alpha_boys = f_alpha_boys m_C
in
let d_boys = d_boys m_C
in
let alpha_er = f_alpha m_C
in
let d_er = s_D m_C
in
let alpha methode =
match methode with
| "Boys"
| "boys" -> {m_alpha = alpha_boys; d = d_boys}
| "ER"
| "er" -> {m_alpha = alpha_er; d = d_er}
| _ -> invalid_arg "Unknown method, please enter Boys or ER"
in
alpha methode;;
(*************************)
(*
m_alpha_d "ER" ;;
m_alpha_d "Boys" ;;
let methode = "ER";;
let alphad = m_alpha_d methode m_C;;
let m_alpha = alphad.m_alpha;;
let d = alphad.d;;
*)
(* Test norme de alpha *)
let norme m =
let vec_m = Mat.as_vec m
in
let vec2 = Vec.sqr vec_m
in sqrt(Vec.sum vec2);;
(*
norme_alpha m_alpha;;
*)
(* Détermination alpha_max et ses indices i et j.
Si alpha max > pi/2 on soustrait pi/2 à la matrice des alphas de manière récursive *)
let rec new_m_alpha m_alpha m_C n_rec_alpha=
let n_mo = Mat.dim2 m_C
in
let alpha_m =
(*Printf.printf "%i\n%!" n_rec_alpha;*)
if n_rec_alpha == 0
then m_alpha
else Mat.init_cols n_mo n_mo (fun i j ->
if (m_alpha.{i,j}) > (3.14 /. 2.)
then (m_alpha.{i,j} -. ( 3.14 /. 2.))
else if m_alpha.{i,j} < -. 3.14 /. 2.
then (m_alpha.{i,j} +. ( 3.14 /. 2.))
else if m_alpha.{i,j} < 0.
then -. m_alpha.{i,j}
else m_alpha.{i,j} )
in
(*Util.debug_matrix "alpha_m" alpha_m;*)
(* Détermination de l'emplacement du alpha max *)
let max_element3 alpha_m =
Mat.as_vec alpha_m
|> iamax
in
(* indice i du alpha max *)
let indice_ii =
let max = max_element3 alpha_m (* Fonction -> constante *)
in
(*Printf.printf "%i\n%!" max;*)
(max - 1) mod n_mo +1
in
(* indice j du alpha max *)
let indice_jj =
let max = max_element3 alpha_m (* Fonction -> constante *)
in
(max - 1) / n_mo +1
in
(* Valeur du alpha max*)
let alpha alpha_m =
let i = indice_ii
in
let j = indice_jj
in
(*Printf.printf "%i %i\n%!" i j;*)
alpha_m.{i,j}
in
let alpha_max = alpha alpha_m (* Fonction -> constante *)
in
(*Printf.printf "%f\n%!" alpha_max;*)
if alpha_max < 3.14 /. 2.
then {alpha_max; indice_ii; indice_jj}
else new_m_alpha alpha_m m_C (n_rec_alpha-1);;
(*************************)
let m_alpha = f_alpha m_C
let alphaij = new_m_alpha m_alpha m_C 3;;
alphaij.alpha_max;;
let f_id loc_deloc =
let id_loc = Mat.identity 2
in
let id_deloc = Mat.init_cols 2 2 (fun i j ->
if i = j
then -.1.
else 0. )
in
let toto loc_deloc =
match loc_deloc with
| "loc" -> id_loc
| "deloc" -> id_deloc
| _ -> invalid_arg "Unknown method, please enter loc or deloc"
in toto loc_deloc;;
f_id "deloc";;
(* Matrice de rotation 2 par 2 *)
let f_R alpha loc_deloc =
let m_id = f_id loc_deloc
in
let m_R =
Mat.init_cols 2 2 (fun i j ->
if i=j
then cos alpha
else if i>j
then sin alpha
else -. sin alpha )
in gemm m_id m_R;;
(*************************)
let alpha = alphaij.alpha_max;; (* Fonction -> constante *)
f_R alpha "loc";;
f_R alpha "deloc";;
(*Uniquement pour pouvoir tester les fonctions après cette cellules*)
(*
(* Indice i et j du alpha max après calcul *)
let indice_i = alphaij.indice_ii;; (* Fonction -> constante *)
let indice_j = alphaij.indice_jj;; (* Fonction -> constante *)
let m_R = f_R alpha;; (* Fonction -> constante *)
*)
(* Fonction d'extraction des 2 vecteurs propres i et j de la matrice des OMs pour les mettres dans la matrice Ksi (n par 2)
pour appliquer R afin d'effectuer la rotation des orbitales *) (* {1,2} -> 1ere ligne, 2e colonne *)
let f_Ksi indice_i indice_j m_C =
let n_ao = Mat.dim1 m_C
in
Mat.init_cols n_ao 2 (fun i j -> if j=1 then m_C.{i,indice_i} else m_C.{i,indice_j} );;
(*************************)
(*
let m_Ksi = f_Ksi indice_i indice_j m_C;; (* Fonction -> constante *)
*)
(* Fonction de calcul de ksi~ (matrice n par 2), nouvelle matrice par application de la matrice de rotation dans laquelle
on obtient les deux orbitales que l'on va réinjecter dans la matrice Phi*)
let f_Ksi_tilde m_R m_Ksi m_C = gemm m_Ksi m_R;;
(*************************)
(*
let m_Ksi_tilde = f_Ksi_tilde m_R m_Ksi;; (* Fonction -> constante *)
*)
(* Pour la réinjection on créer des matrices intérmédiares, une matrice nulle partout sauf sur
les colonnes de i et j et de i~ et j~. On fait la différence de la première matrice avec la matrice
des OMs Phi afin de substituer les colonnes de i et j par des zéro et ensuite sommer cette matrice avec
celle contenant i~ et j~ *)
(* Matrice intérmédiare pour l'injection de ksi~ (i~ et j~) dans la matrice Phi *)
let f_Psi_tilde m_Ksi_tilde indice_i indice_j m_C=
let n_mo = Mat.dim2 m_C
in
let n_ao = Mat.dim1 m_C
in
Mat.init_cols n_ao n_mo (fun i j ->
if j=indice_i
then m_Ksi_tilde.{i,1}
else if j=indice_j then m_Ksi_tilde.{i,2}
else 0.)
;;
(* Matrice intermédiaire pour supprimer ksi (i et j) dans la matrice Phi *)
let f_Psi m_Ksi indice_i indice_j m_C =
let n_mo = Mat.dim2 m_C
in
let n_ao = Mat.dim1 m_C
in
Printf.printf "%i %i\n" n_mo n_ao;
Mat.init_cols n_ao n_mo (fun i j ->
if j=indice_i
then m_Ksi.{i,1}
else if j=indice_j
then m_Ksi.{i,2}
else 0.)
;;
(*************************)
(*
let m_Psi = f_Psi m_Ksi indice_i indice_j;; (* Fonction -> constante *)
let m_Psi_tilde = f_Psi_tilde m_Ksi_tilde indice_i indice_j;; (* Fonction -> constante *)
*)
(* Matrice intérmédiaire où les orbitales i et j ont été supprimées et remplacées par des 0, par soustraction de la matrice Phi
par la matrice *)
let f_interm m_C m_Psi = Mat.sub m_C m_Psi;;
(*************************)
(*
let m_interm = f_interm m_C m_Psi;; (* Fonction -> constante *)
let new_m_C m_C= Mat.add m_Psi_tilde m_interm;;
let m_new_m_C = new_m_C m_C;;
*)
(* Test localisation matrice rectangulaire et partielle *)
(*let toto = [4];;
let occ_m_C m_C toto= Mat.init_cols 4 3 (fun i j ->
if not (List.mem j toto)
then m_C.{i,j}
else 0.);;
let occ = occ_m_C m_C toto;;
*)
(* Fonction créant une liste à partir des éléments manquant d'une autre liste, dans l'intervalle [1 ; n_mo] *)
let miss_elem mat list =
let n_mo = Mat.dim2 mat
in
let vec = Vec.init (n_mo) (fun i ->
if List.mem i list
then 0.
else float_of_int(i))
in
let vec_list = Vec.to_list vec
in
let g a = int_of_float(a)
in
let vec_list_int = List.map g vec_list
in
List.filter (fun x -> x > 0) vec_list_int;;
(* Fonction de séparation d'une matrice en 2 sous matrice, la première matrice correspondant aux colonnes de la matrice dont le numéro est présent
dans la liste et la seconde à celles dont le numéro de colonne n'est pas présent dans la liste *)
let split_mat mat list =
let vec_of_mat = Mat.to_col_vecs mat
in
let f a = vec_of_mat.(a-1)
in
let vec_list_1 = List.map f list
in
let list_2 = miss_elem mat list
in
let vec_list_2 = List.map f list_2
in (Mat.of_col_vecs_list vec_list_1,Mat.of_col_vecs_list vec_list_2);;
m_C;;
let list_om = [1;2]
let m_occ , m_vir = split_mat m_C list_om;;
Boucle localisation avec choix de la méthode de localisation¶
(* Localisation de Edminstion ou de Boys *)
(* Calcul de la nouvelle matrice des coefficient après n rotation d'orbitales *)
let rec final_m_C m_C methode loc_deloc epsilon n prev_critere_D cc=
Printf.printf "%i\n%!" n;
(*Util.debug_matrix "m_C" m_C;*)
if n == 0
then m_C
else
(* Fonction de calcul de la nouvelle matrice de coef après rotation d'un angle alpha *)
let new_m_C m_C methode loc_deloc =
(* Fonction de pattern matching en fonction de la méthode *)
let alphad = m_alpha_d methode m_C
in
(* D critère à maximiser *)
let critere_D = alphad.d
in
Printf.printf "%f\n%!" critere_D;
(* Matrice des alphas *)
let m_alpha = alphad.m_alpha
in
let norme_alpha = norme m_alpha
in
Printf.printf "%f\n%!" norme_alpha;
(*Util.debug_matrix "m_alpha" m_alpha;*)
(* alphaij contient le alpha max ainsi que ses indices i et j *)
let n_rec_alpha = 10 (* Nombre ditération max pour réduire les valeurs de alpha *)
in
let alphaij = new_m_alpha m_alpha m_C n_rec_alpha (* Fonction -> constante *)
in
(* Valeur de alpha max après calcul *) (* Epsilon = Pas <1. , 1. -> normal, sinon Pas plus petit *)
let alpha = epsilon *. alphaij.alpha_max (* Fonction -> constante *)
in
(*Printf.printf "%f\n%!" alpha;*)
(* Indice i et j du alpha max après calcul *)
let indice_i = alphaij.indice_ii (* Fonction -> constante *)
in
let indice_j = alphaij.indice_jj (* Fonction -> constante *)
in
(*Printf.printf "%i %i\n%!" indice_i indice_j;*)
(* Matrice de rotation *)
let m_R = f_R alpha loc_deloc (* Fonction -> constante *)
in
Util.debug_matrix "m_R" m_R;
(* Matrice qui va subir la rotation *)
let m_Ksi = f_Ksi indice_i indice_j m_C (* Fonction -> constante *)
in
(*Util.debug_matrix "m_Ksi" m_Ksi;*)
(* Matrice ayant subit la rotation *)
let m_Ksi_tilde = f_Ksi_tilde m_R m_Ksi m_C (* Fonction -> constante *)
in
(*Util.debug_matrix "m_Ksi_tilde" m_Ksi_tilde;*)
(* Matrice pour supprimerles coef des orbitales i et j dans la matrice des coef *)
let m_Psi = f_Psi m_Ksi indice_i indice_j m_C(* Fonction -> constante *)
in
(*Util.debug_matrix "m_Psi" m_Psi;*)
(* Matrice pour ajouter les coef des orbitales i~ et j~ dans la matrice des coef *)
let m_Psi_tilde = f_Psi_tilde m_Ksi_tilde indice_i indice_j m_C (* Fonction -> constante *)
in
(*Util.debug_matrix "m_Psi_tilde" m_Psi_tilde;*)
(* Matrice avec les coef des orbitales i et j remplacés par 0 *)
let m_interm = f_interm m_C m_Psi (* Fonction -> constante *)
in
(*Util.debug_matrix "m_interm" m_interm;*)
(* Matrice après rotation *)
( Mat.add m_Psi_tilde m_interm, critere_D)
in
let m_new_m_C , critere_D = new_m_C m_C methode loc_deloc(* Fonction -> constante *)
in
let diff = prev_critere_D -. critere_D +. 1.
in
(*Util.debug_matrix "new_alpha_m" (f_alpha m_C);*)
(*Util.debug_matrix "m_new_m_C" m_new_m_C;*)
if diff**2. < cc**2.
then m_new_m_C
else
final_m_C m_new_m_C methode loc_deloc epsilon (n-1) critere_D cc;;
(* Calcul *)
(* Fonction / Matrice des coef / Méthode("Boys" ou "ER") / Localisation ou non ("loc" ou "deloc"/ Pas(<=1.) / Nombre d'itérations max /
0. (valeur de D pour initier la boucle) / critère de convergence sur D*)
let new_m = final_m_C m_C "ER" "loc" 1. 5 0. 10e-7;;
final_m_C new_m "ER" "deloc" 1. 5 0. 10e-7;;
Boucle localisation avec fonctions définies à l'extérieur¶
(* Ancienne boucle
(* Localisation de Edminstion *)
let n_rec_alpha = 10;;
(* Calcul de la nouvelle matrice des coefficient après n rotation d'orbitales *)
let rec final_m_C m_C n =
Printf.printf "%i\n%!" n;
(*Util.debug_matrix "m_C" m_C;*)
if n == 0
then m_C
else
(* Fonction de calcul de la nouvelle matrice de coef après rotation d'un angle alpha *)
let new_m_C m_C =
(* D critère à maximiser *)
let critere_D = s_D m_C (* Fonction -> constante *)
in
Printf.printf "%f\n%!" critere_D;
(* Matrice des alphas *)
let m_alpha = f_alpha m_C (* Fonction -> constante *)
in
(*Util.debug_matrix "m_alpha" m_alpha;*)
(* alphaij contient le alpha max ainsi que ses indices i et j *)
let alphaij = new_m_alpha m_alpha n_rec_alpha (* Fonction -> constante *)
in
(* Valeur de alpha max après calcul *)
let alpha = alphaij.alpha_max (* Fonction -> constante *)
in
(* Indice i et j du alpha max après calcul *)
let indice_i = alphaij.indice_ii (* Fonction -> constante *)
in
let indice_j = alphaij.indice_jj (* Fonction -> constante *)
in
(*Printf.printf "%i %i\n%!" indice_i indice_j;*)
(* Matrice de rotaion *)
let m_R = f_R alpha (* Fonction -> constante *)
in
(*Util.debug_matrix "m_R" m_R;*)
(* Matrice qui va subir la rotation *)
let m_Ksi = f_Ksi indice_i indice_j m_C (* Fonction -> constante *)
in
(*Util.debug_matrix "m_Ksi" m_Ksi;*)
(* Matrice ayant subit la rotation *)
let m_Ksi_tilde = f_Ksi_tilde m_R m_Ksi (* Fonction -> constante *)
in
(*Util.debug_matrix "m_Ksi_tilde" m_Ksi_tilde;*)
(* Matrice pour supprimerles coef des orbitales i et j dans la matrice des coef *)
let m_Psi = f_Psi m_Ksi indice_i indice_j (* Fonction -> constante *)
in
(*Util.debug_matrix "m_Psi" m_Psi;*)
(* Matrice pour ajouter les coef des orbitales i~ et j~ dans la matrice des coef *)
let m_Psi_tilde = f_Psi_tilde m_Ksi_tilde indice_i indice_j (* Fonction -> constante *)
in
(*Util.debug_matrix "m_Psi_tilde" m_Psi_tilde;*)
(* Matrice avec les coef des orbitales i et j remplacés par 0 *)
let m_interm = f_interm m_C m_Psi (* Fonction -> constante *)
in
(* Matrice après rotation *)
Mat.add m_Psi_tilde m_interm
in
let m_new_m_C = new_m_C m_C (* Fonction -> constante *)
in
(*Util.debug_matrix "new_alpha_m" (f_alpha m_C);*)
(*Util.debug_matrix "m_new_m_C" m_new_m_C;*)
final_m_C m_new_m_C (n-1);;
*)
(*
(*Cellule de calcul*)
final_m_C m_C 10;;
*)
(*Définition des fonctions teste*)
(*
(* Localisation de Edminstion *)
(* Définitions de base nécessaire pour la suite *)
let ee_ints = AOBasis.ee_ints ao_basis;;
let m_C = MOBasis.mo_coef mo_basis;;
let n_ao = Mat.dim1 m_C ;;
let n_mo = Mat.dim2 m_C ;;
let sum a =
Array.fold_left (fun accu x -> accu +. x) 0. a
(******************************************************************************************************)
(*Fonction général de calcul des intégrales*)
let integral_general g i j =
Array.map (fun a ->
let v =
Array.map (fun b ->
let u =
Array.map (fun e ->
let t = Array.map (fun f ->
(g a b e f i j) *. ERI.get_phys ee_ints a e b f
) (Util.array_range 1 n_ao)
in sum t
) (Util.array_range 1 n_ao)
in sum u
) (Util.array_range 1 n_ao)
in sum v
) (Util.array_range 1 n_ao)
|> sum
;;
(******************************************************************************************************)
(* Fonction de calcul de tous les alpha -> Matrice, dépend de m_a12, m_b12 qui dépendent de m_C *)
let f_alpha m_C =
(* Fonction de calcul de toutes les intégrales B_12 -> Matrice, dépend de m_C *)
let m_b12 = Mat.init_cols n_ao n_ao (fun i j ->
integral_general (fun a b e f i j ->
( m_C.{a,i} *. m_C.{b,i} -. m_C.{a,j} *. m_C.{b,j} ) *. m_C.{e,i} *. m_C.{f,j}
) i j
)
in
(* Fonction de calcul de toutes les intégrales A_12 -> Matrice, dépend de m_C *)
let m_a12 = Mat.init_cols n_ao n_ao (fun i j ->
integral_general (fun a b e f i j -> m_C.{a,i} *. m_C.{b,j} *. m_C.{e,i} *. m_C.{f,j}
-. 0.25 *. ( m_C.{e,i} *. m_C.{f,i} -. m_C.{e,j} *. m_C.{f,j} )
*. ( m_C.{a,i} *. m_C.{b,i} -. m_C.{a,j} *. m_C.{b,j} )
) i j
)
in
Mat.init_cols n_ao n_ao ( fun i j ->
if i= j
then 0.
else 0.25 *. (acos(-. m_a12.{i,j} /. sqrt((m_a12.{i,j}**2.) +. (m_b12.{i,j}**2.)))))
;;
f_alpha m_C;;
(******************************************************************************************************)
(* Calcul de D *)
let s_D m_C =
let v_D =
let m_D = Mat.init_cols n_ao n_ao (fun i j ->
integral_general (fun a b e f i j -> m_C.{a,i} *. m_C.{b,i} *. m_C.{e,i} *. m_C.{f,i}
) i j
)
in Vec.init n_ao ( fun i -> m_D.{i,1} )
in Vec.sum v_D ;;
s_D m_C;;
(******************************************************************************************************)
(* Si alpha max > pi/2 on doit soustraire pi/2 à la matrice des alphas *)
let m_alpha = f_alpha m_C;;
type alphaij = {
alpha_max : float;
indice_ii : int;
indice_jj : int;}
let rec new_m_alpha m_alpha n_rec_alpha=
let alpha_m =
Printf.printf "%i\n%!" n_rec_alpha;
if n_rec_alpha == 0
then m_alpha
else Mat.init_cols n_ao n_ao (fun i j ->
if (m_alpha.{i,j}) > 3.14 /. 2.
then (m_alpha.{i,j} -. ( 3.14 /. 2.))
else if m_alpha.{i,j} < -. 3.14 /. 2.
then (m_alpha.{i,j} +. ( 3.14 /. 2.))
else if m_alpha.{i,j} < 0.
then -. m_alpha.{i,j}
else m_alpha.{i,j} )
in
Util.debug_matrix "alpha_m" alpha_m;
let max_element3 alpha_m =
Mat.as_vec alpha_m
|> iamax
in
let indice_ii =
let max = max_element3 alpha_m
in
Printf.printf "%i\n%!" max;
(max - 1) mod n_ao +1
in
let indice_jj =
let max = max_element3 alpha_m
in
(max - 1) / n_ao +1
in
let alpha alpha_m =
let i = indice_ii
in
let j = indice_jj
in
alpha_m.{i,j}
in
let alpha_max = alpha alpha_m
in
Printf.printf "%f\n%!" alpha_max;
if alpha_max < 3.14 /. 2.
then {alpha_max; indice_ii; indice_jj}
else new_m_alpha alpha_m (n_rec_alpha-1);;
new_m_alpha m_alpha 10 ;;
let alphaij = new_m_alpha m_alpha 10 ;;
(******************************************************************************************)
let alpha = alphaij.alpha_max
(* Matrice de rotation 2 par 2 *)
let m_R alpha =
Mat.init_cols 2 2 (fun i j -> if i=j then cos alpha
else if i>j then sin alpha
else -. sin alpha);;
(******************************************************************************************)
let indice_i = alphaij.indice_ii;;
let indice_j = alphaij.indice_jj;;
let m_R = m_R alpha;;
(* Fonction d'extraction des 2 vecteurs propres i et j de la matrice des OMs pour les mettres dans la matrice Ksi (n par 2)
pour appliquer R afin d'effectuer la rotation des orbitales *) (* {1,2} -> 1ere ligne, 2e colonne *)
let m_Ksi indice_i indice_j m_C = Mat.init_cols n_ao 2 (fun i j -> if j=1 then m_C.{i,indice_i} else m_C.{i,indice_j} );;
m_Ksi indice_i indice_j m_C;;
let m_Ksi = m_Ksi indice_i indice_j m_C;;
(* Fonction de calcul de ksi~ (matrice n par 2), nouvelle matrice par application de la matrice de rotation dans laquelle
on obtient les deux orbitales que l'on va réinjecter dans la matrice Phi*)
let m_Ksi_tilde m_R m_Ksi = gemm m_Ksi m_R;;
m_Ksi_tilde m_R m_Ksi ;;
(******************************************************************************************)
let m_Ksi_tilde = m_Ksi_tilde m_R m_Ksi
(* Pour la réinjection on créer des matrices intérmédiares, une matrice nulle partout sauf sur
les colonnes de i et j et de i~ et j~. On fait la différence de la première matrice avec la matrice
des OMs Phi afin de substituer les colonnes de i et j par des zéro et ensuite sommer cette matrice avec
celle contenant i~ et j~ *)
(* Matrice intérmédiare pour l'injection de ksi~ (i~ et j~) dans la matrice Phi *)
let m_Psi_tilde m_Ksi_tilde indice_i indice_j = Mat.init_cols n_ao n_ao (fun i j -> if j=indice_i then m_Ksi_tilde.{i,1}
else if j=indice_j then m_Ksi_tilde.{i,2}
else 0.);;
m_Psi_tilde m_Ksi_tilde indice_i indice_j;;
(* Matrice intermédiaire pour supprimer ksi (i et j) dans la matrice Phi *)
let m_Psi m_Ksi indice_i indice_j = Mat.init_cols n_ao n_ao (fun i j -> if j=indice_i then m_Ksi.{i,1}
else if j=indice_j then m_Ksi.{i,2}
else 0.);;
m_Psi m_Ksi indice_i indice_j;;
(******************************************************************************************)
let m_Psi = m_Psi m_Ksi indice_i indice_j;;
let m_Psi_tilde = m_Psi_tilde m_Ksi_tilde indice_i indice_j;;
(* Matrice intérmédiaire où les orbitales i et j ont été supprimées et remplacées par des 0, par soustraction de la matrice Phi
par la matrice *)
let m_interm m_C m_Psi = Mat.sub m_C m_Psi;;
let m_interm = m_interm m_C m_Psi;;
(* Construction de la nouvelle matrice d'OMs phi~ *)
let m_Phi_tilde m_Psi_tilde m_interm = Mat.add m_Psi_tilde m_interm;;
m_Phi_tilde m_Psi_tilde m_interm;;
*)
Programmes avec fonctions définies à l'intérieurs¶
(* Localisation de Edminstion *)
(*Fonctionne -> programme avec les fonctions définies à l'intérieur*)
(*
(* Définitions de base nécessaire pour la suite *)
let ee_ints = AOBasis.ee_ints ao_basis;;
let m_C = MOBasis.mo_coef mo_basis;;
let n_ao = Mat.dim1 m_C ;;
let n_mo = Mat.dim2 m_C ;;
let sum a =
Array.fold_left (fun accu x -> accu +. x) 0. a
(******************************************************************************************************)
(*Fonction général de calcul des intégrales*)
let integral_general g i j =
Array.map (fun a ->
let v =
Array.map (fun b ->
let u =
Array.map (fun e ->
let t = Array.map (fun f ->
(g a b e f i j) *. ERI.get_phys ee_ints a e b f
) (Util.array_range 1 n_ao)
in sum t
) (Util.array_range 1 n_ao)
in sum u
) (Util.array_range 1 n_ao)
in sum v
) (Util.array_range 1 n_ao)
|> sum
;;
type alphaij = {
alpha_max : float;
indice_ii : int;
indice_jj : int;};;
let n_rec_alpha = 10;;
(*
let alpha = 1.;;
let y_R alpha =
Mat.init_cols 2 2 (fun i j -> if i=j then cos alpha
else if i>j then sin alpha
else -. sin alpha);;
let y_R = y_R alpha;;
let m_C = gemm m_C y_R;;
*)
(******************************************************************************************************)
let rec final_m_C m_C n =
Printf.printf "%i\n%!" n;
(*
Util.debug_matrix "m_C" m_C;
*)
if n == 0 then m_C
else
let new_m_C m_C =
(* Fonction de calcul de tous les alpha -> Matrice, dépend de m_a12, m_b12 qui dépendent de m_C *)
let f_alpha m_C =
(* Fonction de calcul de toutes les intégrales B_12 -> Matrice, dépend de m_C *)
let m_b12 = Mat.init_cols n_ao n_ao (fun i j ->
integral_general (fun a b e f i j ->
( m_C.{a,i} *. m_C.{b,i} -. m_C.{a,j} *. m_C.{b,j} ) *. m_C.{e,i} *. m_C.{f,j}
) i j
)
in
(* Fonction de calcul de toutes les intégrales A_12 -> Matrice, dépend de m_C *)
let m_a12 = Mat.init_cols n_ao n_ao (fun i j ->
integral_general (fun a b e f i j -> m_C.{a,i} *. m_C.{b,j} *. m_C.{e,i} *. m_C.{f,j}
-. 0.25 *. (( m_C.{e,i} *. m_C.{f,i} -. m_C.{e,j} *. m_C.{f,j} )
*. ( m_C.{a,i} *. m_C.{b,i} -. m_C.{a,j} *. m_C.{b,j} ))
) i j
)
in
Mat.init_cols n_ao n_ao ( fun i j -> if i= j
then 0.
else
0.25 *. (acos(-. m_a12.{i,j} /. sqrt((m_a12.{i,j}**2.) +. (m_b12.{i,j}**2.)))))
in
(* Calcul de D *)
let s_D m_C =
let v_D =
let m_D = Mat.init_cols n_ao n_ao (fun i j ->
integral_general (fun a b e f i j -> m_C.{a,i} *. m_C.{b,i} *. m_C.{e,i} *. m_C.{f,i}
) i j
)
in Vec.init n_ao ( fun i -> m_D.{i,1} )
in Vec.sum v_D
in
let critere_D = s_D m_C (* Fonction -> constante *)
in
Printf.printf "%f\n%!" critere_D;
let m_alpha = f_alpha m_C (* Fonction -> constante *)
in
(*
Util.debug_matrix "m_alpha" m_alpha;
*)
(* Détermination alpha_max et ses indices i et j.
Si alpha max > pi/2 on soustrait pi/2 à la matrice des alphas de manière récursive *)
let rec new_m_alpha m_alpha n_rec_alpha=
let alpha_m =
Printf.printf "%i\n%!" n_rec_alpha;
if n_rec_alpha == 0
then m_alpha
else Mat.init_cols n_ao n_ao (fun i j ->
if (m_alpha.{i,j}) > (3.14 /. 2.)
then (m_alpha.{i,j} -. ( 3.14 /. 2.))
else if m_alpha.{i,j} < -. 3.14 /. 2.
then (m_alpha.{i,j} +. ( 3.14 /. 2.))
else if m_alpha.{i,j} < 0.
then -. m_alpha.{i,j}
else m_alpha.{i,j} )
in
Util.debug_matrix "alpha_m" alpha_m;
(* Détermination de l'emplacement du alpha max *)
let max_element3 alpha_m =
Mat.as_vec alpha_m
|> iamax
in
(* indice i du alpha max *)
let indice_ii =
let max = max_element3 alpha_m (* Fonction -> constante *)
in
(*
Printf.printf "%i\n%!" max;
*)
(max - 1) mod n_ao +1
in
(* indice j du alpha max *)
let indice_jj =
let max = max_element3 alpha_m (* Fonction -> constante *)
in
(max - 1) / n_ao +1
in
(* Valeur du alpha max*)
let alpha alpha_m =
let i = indice_ii
in
let j = indice_jj
in
(*
Printf.printf "%i %i\n%!" i j;
*)
alpha_m.{i,j}
in
let alpha_max = alpha alpha_m (* Fonction -> constante *)
in
Printf.printf "%f\n%!" alpha_max;
if alpha_max < 3.14 /. 2.
then {alpha_max; indice_ii; indice_jj}
else new_m_alpha alpha_m (n_rec_alpha-1)
in
let alphaij = new_m_alpha m_alpha n_rec_alpha (* Fonction -> constante *)
in
(* Valeur de alpha max après calcul *)
let alpha = alphaij.alpha_max (* Fonction -> constante *)
in
(* Matrice de rotation 2 par 2 *)
let f_R alpha =
Mat.init_cols 2 2 (fun i j -> if i=j then cos alpha
else if i>j then sin alpha
else -. sin alpha )
in
(* Indice i et j du alpha max après calcul *)
let indice_i = alphaij.indice_ii (* Fonction -> constante *)
in
let indice_j = alphaij.indice_jj (* Fonction -> constante *)
in
Printf.printf "%i %i\n%!" indice_i indice_j;
let m_R = f_R alpha (* Fonction -> constante *)
in
Util.debug_matrix "m_R" m_R;
(* Fonction d'extraction des 2 vecteurs propres i et j de la matrice des OMs pour les mettres dans la matrice Ksi (n par 2)
pour appliquer R afin d'effectuer la rotation des orbitales *) (* {1,2} -> 1ere ligne, 2e colonne *)
let f_Ksi indice_i indice_j m_C = Mat.init_cols n_ao 2 (fun i j -> if j=1 then m_C.{i,indice_i} else m_C.{i,indice_j} )
in
let m_Ksi = f_Ksi indice_i indice_j m_C (* Fonction -> constante *)
in
Util.debug_matrix "m_Ksi" m_Ksi;
(* Fonction de calcul de ksi~ (matrice n par 2), nouvelle matrice par application de la matrice de rotation dans laquelle
on obtient les deux orbitales que l'on va réinjecter dans la matrice Phi*)
let f_Ksi_tilde m_R m_Ksi = gemm m_Ksi m_R
in
let m_Ksi_tilde = f_Ksi_tilde m_R m_Ksi (* Fonction -> constante *)
in
Util.debug_matrix "m_Ksi_tilde" m_Ksi_tilde;
(* Pour la réinjection on créer des matrices intérmédiares, une matrice nulle partout sauf sur
les colonnes de i et j et de i~ et j~. On fait la différence de la première matrice avec la matrice
des OMs Phi afin de substituer les colonnes de i et j par des zéro et ensuite sommer cette matrice avec
celle contenant i~ et j~ *)
(* Matrice intérmédiare pour l'injection de ksi~ (i~ et j~) dans la matrice Phi *)
let f_Psi_tilde m_Ksi_tilde indice_i indice_j = Mat.init_cols n_ao n_ao (fun i j -> if j=indice_i then m_Ksi_tilde.{i,1}
else if j=indice_j then m_Ksi_tilde.{i,2}
else 0.)
in
(* Matrice intermédiaire pour supprimer ksi (i et j) dans la matrice Phi *)
let f_Psi m_Ksi indice_i indice_j = Mat.init_cols n_ao n_ao (fun i j -> if j=indice_i then m_Ksi.{i,1}
else if j=indice_j then m_Ksi.{i,2}
else 0.)
in
let m_Psi = f_Psi m_Ksi indice_i indice_j (* Fonction -> constante *)
in
Util.debug_matrix "m_Psi" m_Psi;
let m_Psi_tilde = f_Psi_tilde m_Ksi_tilde indice_i indice_j (* Fonction -> constante *)
in
Util.debug_matrix "m_Psi_tilde" m_Psi_tilde;
(* Matrice intérmédiaire où les orbitales i et j ont été supprimées et remplacées par des 0, par soustraction de la matrice Phi
par la matrice *)
let f_interm m_C m_Psi = Mat.sub m_C m_Psi
in
let m_interm = f_interm m_C m_Psi (* Fonction -> constante *)
in
Mat.add m_Psi_tilde m_interm
in
let m_new_m_C = new_m_C m_C (* Fonction -> constante *)
in
Util.debug_matrix "new_alpha_m" (f_alpha m_C);
Util.debug_matrix "m_new_m_C" m_new_m_C;
final_m_C m_new_m_C (n-1);;
(*****************************)
final_m_C m_C 10;;
*)
OLD ->¶
let ints = MOBasis.ee_ints mo_basis in
ERI.get_phys ints 1 2 1 2;;