StageYann/Work.ipynb

86 KiB
Raw Permalink Blame History

None <html> <head> </head>

Installation de QCaml

  1. Clonage de QCaml:
    git clone https://gitlab.com/scemama/QCaml.git
    
  2. Installation des dependances:
    opam install ocamlbuild ocamlfind lacaml gnuplot getopt alcotest zarith
    cd QCaml
    ./configure
    
  3. Compilation
    make
    

Liens utiles

Initialisation

Bloc a executer avant de pouvoir utiliser QCaml dans le Notebook

In [ ]:
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$_n$

In [ ]:
(* 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;;
*)
let xyz_string = 
"8                                                                                            
C1
C   0.000000    0.000000    0.424165
H   0.000000    0.000000    1.508704
N   0.000000    1.158087   -0.174215
N   0.000000   -1.158087   -0.174215
H   0.000000    1.258360   -1.178487
H   0.000000    2.005112    0.371143
H   0.000000   -2.005112    0.371143
H   0.000000   -1.258360   -1.178487
"

Base atomique

Les bases atomiques sont telechargeables ici: https://www.basissetexchange.org/

On telecharge la base cc-pvdz depuis le site:

In [ ]:
let basis_string = 
"HYDROGEN
S   3
1         0.1873113696E+02       0.3349460434E-01
2         0.2825394365E+01       0.2347269535E+00
3         0.6401216923E+00       0.8137573261E+00
S   1
1         0.1612777588E+00       1.0000000

CARBON
S   6
1         0.3047524880E+04       0.1834737132E-02
2         0.4573695180E+03       0.1403732281E-01
3         0.1039486850E+03       0.6884262226E-01
4         0.2921015530E+02       0.2321844432E+00
5         0.9286662960E+01       0.4679413484E+00
6         0.3163926960E+01       0.3623119853E+00
S   3
1         0.7868272350E+01      -0.1193324198E+00
2         0.1881288540E+01      -0.1608541517E+00
3         0.5442492580E+00       0.1143456438E+01
S   1
1         0.1687144782E+00       0.1000000000E+01
P   3
1         0.7868272350E+01       0.6899906659E-01
2         0.1881288540E+01       0.3164239610E+00
3         0.5442492580E+00       0.7443082909E+00
P   1
1         0.1687144782E+00       0.1000000000E+01

NITROGEN
S   6
1         0.4173511460E+04       0.1834772160E-02
2         0.6274579110E+03       0.1399462700E-01
3         0.1429020930E+03       0.6858655181E-01
4         0.4023432930E+02       0.2322408730E+00
5         0.1282021290E+02       0.4690699481E+00
6         0.4390437010E+01       0.3604551991E+00
S   3
1         0.1162636186E+02      -0.1149611817E+00
2         0.2716279807E+01      -0.1691174786E+00
3         0.7722183966E+00       0.1145851947E+01
S   1
1         0.2120314975E+00       0.1000000000E+01
P   3
1         0.1162636186E+02       0.6757974388E-01
2         0.2716279807E+01       0.3239072959E+00
3         0.7722183966E+00       0.7408951398E+00
P   1
1         0.2120314975E+00       0.1000000000E+01
"
(*"HYDROGEN
S   3
1         0.1873113696E+02       0.3349460434E-01
2         0.2825394365E+01       0.2347269535E+00
3         0.6401216923E+00       0.8137573261E+00
S   1
1         0.1612777588E+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$.

In [ ]:
let nuclei =
  Nuclei.of_xyz_string xyz_string
  
let basis = 
  Basis.of_nuclei_and_basis_string nuclei basis_string
  
let simulation = 
  Simulation.make ~charge:1 ~multiplicity:1 ~nuclei basis
  
let ao_basis = 
  Simulation.ao_basis simulation
  
let nocc =
    let elec = Simulation.electrons simulation in
    Electrons.n_alfa elec
    
let n_core =
    (Nuclei.small_core @@ Simulation.nuclei @@ MOBasis.simulation mo_basis) / 2
In [ ]:
(*
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
  
let nocc =
    let elec = Simulation.electrons simulation in
    Electrons.n_alfa elec
*)

Plot des orbitales atomiques

In [ ]:
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

In [ ]:
let hf = HartreeFock.make simulation
In [ ]:
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:

In [ ]:
let mo_coef = MOBasis.mo_coef mo_basis
In [ ]:
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.
\begin{eqnarray} J_{ij} & = & \sum_{kl} P_{kl} \langle i k | j l \rangle \\ K_{il} & = & \sum_{kj} P_{kj} \langle i k | j l \rangle \end{eqnarray}

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

In [ ]:
(*
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}$.

In [ ]:
(*
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 = m_assemble_boys
(*
    match Guess.make ~nocc ~guess:`Hcore ao_basis with
    | Hcore m_H -> c_of_h m_H
    | _ -> assert false
  *)  
  *)

On construit la matrice densite

In [ ]:
(*
let m_P = 
    (*let m_C = m_assemble_er in*)
  (*  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$:

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)
*)

On construit l'operateur de Fock: $ F = H_c + J - K $

In [ ]:
(*
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.

In [ ]:
(*
let m_C = m_assemble_boys
(*  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:

In [ ]:
(*
let energy =
   (Simulation.nuclear_repulsion simulation) +. 0.5 *.
   Mat.gemm_trace  m_P  (Mat.add  m_Hc  m_F)
*)

On itere:

In [ ]:
(*
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 $$

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}] $

$\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}$

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 des orbitales moléculaires

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 coefficients des OMs $\Phi$, (m_C), les orbitales $i$ et $j$ (vecteurs colonnes) pour former une nouvelle matrice $Ksi$ de dimension (n par 2).

Pour effectuer la rotation des orbitales $i$ et $j$ on utilise la matrice de rotation $R$ (m_R) pour la rotation de 2 orbitales, qui est définie comme la matrice :

$R$ =

  ( cos(alpha)   -sin(alpha) )

  ( sin(alpha)    cos(alpha) )

La valeur de $\alpha$ sera la plus grande de la matrice des alphas Cf Localisation de Edminston Ruedenberg

On applique $R$ à $Khi$ : $R Ksi = \tilde Ksi$

On obtient $\tilde Ksi$ la matrice contenant les coefficients des deux nouvelles OMs $\tilde i$ et $\tilde j$ obtenues par rotation de $i$ et $j$.

On réinjecte ces deux nouvelles orbitales $\tilde i$ et $\tilde j$ à la place des anciennes orbitales $i$ et $j$ dans la matrice des coefficients des OMs $\Phi$, (m_C), ce qui nous donne une nouvelle matrice des coefficient des OMs $\tilde \Phi$, (new_m_C). 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 $\tilde \Psi$ (n par n) avec tous ses éléments nuls sauf les colonnes qui contiennent les OMs $\tilde i$ et $\tilde j$
  • une matrice m_interm (n par n) où l'on a soustrait $\Psi$ à $\Phi$ pour créer des 0 sur les colonnes des OMs $i$ et $j$

Ainsi, on soustrait à la matrice des coefficient $\Phi$ la matrice $\Psi$ pour supprimer les OMs $i$ et $j$ de celle ci puis on additionne cette nouvelle matrice à la matrice $\tilde \Psi$ pour créer la nouvelle matrice des coefficients des OMS $\tilde \Phi$, avec $\tilde i$ et $\tilde j$.

On repart de cette nouvelle matrice des coefficients $\tilde \Phi$ et on cherche la paire d'orbitale $(k,l)$ ayant le plus grand angle de rotation alpha (l'angle $\alpha$ pour la paire d'orbitale $(i,j)$ étant devenui nul après rotation. 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 la localisation de Boyls.

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), 457464. doi:10.1103/revmodphys.35.457

Méthode :

Le but de cette méthode est de maximiser $D$, c'est à dire le terme de répulsion :

$D(\phi)= \sum_n [\phi_n^2 | \phi_n^2 ]$

$= \sum_n < \phi^2_n | 1/r_{12} | \phi^2_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 $\tilde i$ et $\tilde 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 ] $

Pour le calcul de $D$ , 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])$

Localisation de Boys

Ref: Localized molecular orbitals for polyatomic molecules. I. A comparison of the EdmistonRuedenberg and Boys localization methods J. Chem. Phys. 61, 3905 (1974); https://doi.org/10.1063/1.1681683 Daniel A. Kleier, Thomas A. Halgren, John H. Hall Jr., and William N. Lipscomb

On procède comme pour la méthode de 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 D à maximiser est défini tel que :

$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 >$

Calculs : Localisation de Edminston

Fonctions

In [ ]:
(* 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
    
let pi = acos(-1.);;
In [ ]:
(*

(*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;;      
*)
*)
In [ ]:
(* 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 v_d = Vec.init n_mo (fun i -> 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);
                  v_d.{j} <- v_d.{j} +.  m_ijk.{j,j,j} *. m_C.{s,j}
         ) (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 if  +. 0.25 *. atan2 m_b12.{i,j} m_a12.{i,j} <= 0. 
        then  pi /. 4. +. 0.25 *. atan2 m_b12.{i,j} m_a12.{i,j}
        else -. pi /. 4. +. 0.25 *. atan2 m_b12.{i,j} m_a12.{i,j})
    ,Vec.sum v_d);;

(*********************)
(*
f_alpha m_C;;*)
(*let m_alpha , s_D = f_alpha m_C;;*)
In [ ]:
(* Fonction calcul alpha Boys *)
let f_alpha_boys m_C = 
    let multipoles = AOBasis.multipole ao_basis in
    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 if  +. 0.25 *. atan2 m_b12.{i,j} m_a12.{i,j} >= 0.
            then pi /. 4. -. 0.25 *. atan2 m_b12.{i,j} m_a12.{i,j}
            else -. pi /. 4. -. 0.25 *. atan2 m_b12.{i,j} m_a12.{i,j})
        ,Vec.sum(Vec.init n_mo ( fun i -> (phi_x_phi.{i,i})**2. +. (phi_y_phi.{i,i})**2. +. (phi_z_phi.{i,i})**2.)));;

(****************************)
(*
f_alpha_boys m_C;;
*)
In [ ]:
(* Test méthode de boys *)

    let f_theta m_C = 
        let multipoles = AOBasis.multipole ao_basis in
        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 phi_x2_phi =
              Multipole.matrix_x2 multipoles 
              |> MOBasis.mo_matrix_of_ao_matrix ~mo_coef:m_C
        in    
        let phi_y2_phi =
              Multipole.matrix_y2 multipoles 
              |> MOBasis.mo_matrix_of_ao_matrix ~mo_coef:m_C
        in
        let phi_z2_phi =
              Multipole.matrix_z2 multipoles 
              |> MOBasis.mo_matrix_of_ao_matrix ~mo_coef:m_C
        in
        let m_b_0 =
            let b_0 g h = Mat.init_cols n_mo n_mo (fun i j ->
                h.{i,i} +. h.{j,j} -. (g.{i,i}**2. +. g.{j,j}**2.))
            in Mat.add (b_0 phi_x_phi phi_x2_phi) (Mat.add (b_0 phi_y_phi phi_y2_phi) (b_0 phi_z_phi phi_z2_phi))  
        in
        let m_beta = 
            let beta g = Mat.init_cols n_mo n_mo (fun i j ->
                (g.{i,i} -. g.{j,j})**2. -. 4. *. g.{i,j}**2.)
            in Mat.add (beta phi_x_phi) (Mat.add (beta phi_y_phi) (beta phi_z_phi))
        in (*Util.debug_matrix "m_beta" m_beta;*)


        let m_gamma = 
            let gamma g = Mat.init_cols n_mo n_mo (fun i j ->
                4. *. g.{i,j} *. (g.{i,i} -. g.{j,j}) )
            in Mat.add (gamma phi_x_phi) (Mat.add (gamma phi_y_phi) (gamma phi_z_phi))
        in (*Util.debug_matrix "m_gamma" m_gamma;*)

        let m_theta = Mat.init_cols n_mo n_mo (fun i j ->
            if i = j 
            then 0.
            else +. 0.25 *. atan2 m_gamma.{i,j} m_beta.{i,j})
        in (*Util.debug_matrix "m_theta" m_theta;*)

        let m_critere_B = Mat.init_cols n_mo n_mo (fun i j ->
            m_b_0.{i,j} +. 0.25 *. ((1. -. cos(4. *. m_theta.{i,j})) *. m_beta.{i,j} +. sin(4. *. m_theta.{i,j}) *. m_gamma.{i,j}))
        in m_theta, Vec.sum(Mat.as_vec m_critere_B)
;;
(*
f_theta m_C;;*)
In [ ]:
(* 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 methode =
    
        match methode with 
            | "Boys_er"
            | "boys_er" -> let alpha_boys , d_boys  = f_alpha_boys m_C in {m_alpha = alpha_boys; d = d_boys}
            | "BOYS" -> let theta_boys, b_boys = f_theta m_C in {m_alpha = theta_boys; d = 1. /. b_boys}
            | "ER"
            | "er" -> let alpha_er , d_er = f_alpha m_C in {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 = "BOYS"
let alphad = m_alpha_d methode m_C;;
let m_alpha = alphad.m_alpha;;
let d = alphad.d;;
*)
In [ ]:
(* 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;;
*)
In [ ]:
type alphaij = {
    alpha_max : float;
    indice_ii : int;
    indice_jj : int;};;

(* 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}) >= (pi /. 2.) 
                    then (m_alpha.{i,j} -. ( pi /. 2.))
                else if m_alpha.{i,j} <= -. pi /. 2.
                    then (m_alpha.{i,j} +. ( pi /. 2.))
                    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 et j du alpha max *)
    let indice_ii, indice_jj = 
        let max = max_element3 alpha_m 
        in
        (max - 1) mod n_mo +1, (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 
    in
    
    (*Printf.printf "%f\n%!" alpha_max;*)
    
    if alpha_max < pi /. 2.
        then {alpha_max; indice_ii; indice_jj}
        else new_m_alpha alpha_m m_C (n_rec_alpha-1);;

(*************************)
(*
let m_alpha,d = f_theta m_C
let alphaij = new_m_alpha m_alpha m_C 3;;
alphaij.alpha_max;;
*)
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 )
;;
(*************************)
(*
let alpha =  alphaij.alpha_max;;  (* Fonction -> constante *)        
f_R alpha;;
*)
In [ ]:
(* Matrice de rotation n par n *)
    let new_c m_C angle= 
        let n_mo =  Mat.dim2 m_C in

        (* Antisymétrisation et 0 sur la diagonale*)
        let m_angle angle= Mat.init_cols n_mo n_mo (fun i j ->
           if i = j 
                then 0.
            else if i>j 
                then -. angle
                else angle) in

                    (*Util.debug_matrix "m" m;*)
        let m = m_angle angle in
        (* Mise au carré -> X² *)
        let mm = gemm m m in

                (*Util.debug_matrix "mm" mm;*)

        (* Diagonalisation de X² -> diag = vecteur contenant les eigenvalues (-tau²),
        m_angle2 -> Matrice avec les eigenvectors (W) *)
        let diag = syev ~vectors:true mm in
        (* Passage de -tau² à tau² *)
        let square_tau= Vec.abs diag in
        (* Passage de tau² à tau *)
        let tau = Vec.sqrt square_tau in 
        (* Calcul de cos tau à partir du vecteur tau *)
        let cos_tau = Vec.cos tau in
        (* Matrice cos tau *)
        let m_cos_tau = Mat.init_cols n_mo n_mo (fun i j -> if i=j then cos_tau.{i} else 0.) in

                    (*Util.debug_matrix "m_cos_tau" m_cos_tau;*)

        (* Calcul de sin tau à partir du vecteur tau *)
        let sin_tau = Vec.sin tau in 
        (* Matrice de sin tau *)
        let m_sin_tau = Mat.init_cols n_mo n_mo (fun i j -> if i=j then sin_tau.{i} else 0.) in

                    (*Util.debug_matrix "m_sin_tau" m_sin_tau;*)

        (* Calcul de la transposée de W (m_angles2) *)
        let transpose_mm = Mat.transpose_copy mm in

                    (*Util.debug_matrix "transpose_mm" transpose_mm;*)

        (* Calcul du vecteur tau^-1 *)
        let tau_1 = Vec.init n_mo (fun i -> if tau.{i}<= 0.001 then 1.
        else 1. /. tau.{i}) in
        (* Calcul de la matrice tau^⁻1 *)
        let m_tau_1 = Mat.init_cols n_mo n_mo (fun i j -> if i=j then tau_1.{i} else 0.) in

                     (*Util.debug_matrix "m_tau_1" m_tau_1;*)

        (* gemm mm (gemm m_cos_tau transpose_mm) -> W cos(tau) Wt *)
        let a = gemm mm (gemm m_cos_tau transpose_mm) in

                    (*Util.debug_matrix "a" a;*)

        (* gemm mm (gemm m_tau_1 ( gemm m_sin_tau (gemm transpose_mm m))) -> W tau^-1 sin(tau) Wt X *)
        let b = gemm mm (gemm m_tau_1 ( gemm m_sin_tau (gemm transpose_mm m))) in

                    (*Util.debug_matrix "b" b;*)

        (* Somme de 2 matrices, ici -> a + b -> R *)
        let m_r = Mat.add a b (* Matrice de rotation R *)
    in gemm m_C m_r
;;

(************************************************)

(*
f_alpha_boys m_C;;
f_alpha_boys new_c;;
*)

(********************************************)

let f_r alphaij m_C = 
    let n_mo =  Mat.dim2 m_C in
    
    let indice_i = alphaij.indice_ii in
    let indice_j = alphaij.indice_jj in
    let alpha = alphaij.alpha_max in
    (* Matrice des angles -> X *)
    let m_angle = Mat.init_cols n_mo n_mo (fun i j -> 
        if (i = indice_i && j = indice_j) || (j = indice_i && i = indice_j) 
            then alpha
            else 0.) in
          
           (*Util.debug_matrix "m_angle" m_angle;*)
    
    (*
    let m_alpha = alphad.m_alpha in
    let m_angle = Mat.init_cols n_mo n_mo (fun i j -> 
        0.1 *. m_alpha.{i,j})
    in
    *)
    (* Antisymétrisation et 0 sur la diagonale*)
    let m =Mat.init_cols n_mo n_mo (fun i j ->
       if i = j 
            then 0.
        else if i>j 
            then m_angle.{i,j} 
            else -. m_angle.{i,j})
            
            in
               
                (*Util.debug_matrix "m" m;*)
            
    (* Mise au carré -> X² *)
    let mm = gemm m m in
    
            (*Util.debug_matrix "mm" mm;*)
            
    (* Diagonalisation de X² -> diag = vecteur contenant les eigenvalues (-tau²),
    m_angle2 -> Matrice avec les eigenvectors (W) *)
    let diag = syev ~vectors:true mm in
    (* Passage de -tau² à tau² *)
    let square_tau= Vec.abs diag in
    (* Passage de tau² à tau *)
    let tau = Vec.sqrt square_tau in 
    (* Calcul de cos tau à partir du vecteur tau *)
    let cos_tau = Vec.cos tau in
    (* Matrice cos tau *)
    let m_cos_tau = Mat.init_cols n_mo n_mo (fun i j -> if i=j then cos_tau.{i} else 0.) in
    
                (*Util.debug_matrix "m_cos_tau" m_cos_tau;*)
                
    (* Calcul de sin tau à partir du vecteur tau *)
    let sin_tau = Vec.sin tau in 
    (* Matrice de sin tau *)
    let m_sin_tau = Mat.init_cols n_mo n_mo (fun i j -> if i=j then sin_tau.{i} else 0.) in
    
                (*Util.debug_matrix "m_sin_tau" m_sin_tau;*)
                
    (* Calcul de la transposée de W (m_angles2) *)
    let transpose_mm = Mat.transpose_copy mm in
    
                (*Util.debug_matrix "transpose_mm" transpose_mm;*)
                
    (* Calcul du vecteur tau^-1 *)
    let tau_1 = Vec.init n_mo (fun i -> if tau.{i}<= 0.001 then 1.
    else 1. /. tau.{i}) in
    (* Calcul de la matrice tau^⁻1 *)
    let m_tau_1 = Mat.init_cols n_mo n_mo (fun i j -> if i=j then tau_1.{i} else 0.) in
    
                 (*Util.debug_matrix "m_tau_1" m_tau_1;*)
                 
    (* gemm mm (gemm m_cos_tau transpose_mm) -> W cos(tau) Wt *)
    let a = gemm mm (gemm m_cos_tau transpose_mm) in
    
                (*Util.debug_matrix "a" a;*)
                
    (* gemm mm (gemm m_tau_1 ( gemm m_sin_tau (gemm transpose_mm m))) -> W tau^-1 sin(tau) Wt X *)
    let b = gemm mm (gemm m_tau_1 ( gemm m_sin_tau (gemm transpose_mm m))) in
    
                (*Util.debug_matrix "b" b;*)
                
    (* Somme de 2 matrices, ici -> a + b -> R *)
Mat.add a b (* Matrice de rotation R *)
;;

(*******************************************************************)  

(*
let m_r =  f_r alphaij m_C;;
gemm m_C m_r;;
*)
In [ ]:
(*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;; 
let indice_j = alphaij.indice_jj;;   
let m_R = f_R alpha;;  
*)
In [ ]:
(* 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;; 
*)
In [ ]:
(* 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 m_C;; 
*)
In [ ]:
(* Fonction pour la création de matrice intermédiaire pour supprimer i j et ajouter i~ et j~ *)
let f_k mat 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 mat.{i,1}
    else if j=indice_j 
        then mat.{i,2}
        else 0.)
(*************************)
(*
let m_Psi = f_k m_Ksi indice_i indice_j m_C;; 
let m_Psi_tilde = f_k m_Ksi_tilde indice_i indice_j m_C;; 
*)
In [ ]:
(* 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;; 
let new_m_C m_C= Mat.add m_Psi_tilde m_interm;;
let m_new_m_C = new_m_C m_C;;
*)

Boucle localisation avec choix de la méthode de localisation

In [ ]:
(* Localisation de Edminstion ou de Boys *)

(* Calcul de la nouvelle matrice des coefficient après n rotation d'orbitales *)
let rec localisation m_C methode epsilon n prev_critere_D prev_m_alpha cc max_D=

    (*Printf.printf "%i\n%!" n;*)
    (*Printf.printf "%f\n%!" epsilon;*)
    (*Util.debug_matrix "m_C" m_C;
    Util.debug_matrix "new_alpha_m" prev_m_alpha;*)

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 m_alpha epsilon =
        (*
        let alphaij = new_m_alpha m_alpha m_C 3 in
        let m_r =  f_r alphaij m_C in
        (*Util.debug_matrix "m_r" m_r;*)
        let new_m = gemm m_C m_r in
        (*Util.debug_matrix "new_m" new_m;*)
        
        new_m, alphaij.alpha_max
        *)
        
        (* alphaij contient le alpha max ainsi que ses indices i et j *)
        let n_rec_alpha = 10 in (* Nombre ditération max pour réduire les valeurs de alpha *)
        let alphaij = new_m_alpha m_alpha m_C n_rec_alpha in

        (* Valeur de alpha max après calcul *) (* Epsilon = Pas <1. , 1. -> normal, sinon Pas plus petit *)
        let alpha = (alphaij.alpha_max) *. epsilon in
        
            (*Printf.printf "%f\n%!" alpha;*)

        (* Indice i et j du alpha max après calcul *)
        let indice_i = alphaij.indice_ii in
        let indice_j = alphaij.indice_jj in

            (*Printf.printf "%i %i\n%!" indice_i indice_j;*)
        
        (* Matrice de rotation *)
        let m_R = f_R alpha 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 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 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_k m_Ksi indice_i indice_j m_C 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_k m_Ksi_tilde indice_i indice_j m_C 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 in

            (*Util.debug_matrix "m_interm" m_interm;*)

        (* Matrice après rotation *)
        (Mat.add m_Psi_tilde m_interm, alpha)
        
    in
    (* Extraction de la nouvelle matrice des coef et du alpha_max *)
    let m_new_m_C, alpha_max = new_m_C m_C methode prev_m_alpha epsilon in

    (* Fonction de pattern matching en fonction de la méthode *)
    let alphad = m_alpha_d methode m_new_m_C in
               
    (* Matrice des alphas *)
    let m_alpha = alphad.m_alpha in
    
    (* Critères possibles de localisation -> norme_alpha, moyenne_alpha, alpha, diff *)
    
    (* Norme de m_alpha, moyenne et max  *)
    
    let norme_alpha = norme m_alpha in
    let moyenne_alpha = norme_alpha /. (float_of_int((Mat.dim1 m_C)* (Mat.dim2 m_C))) in
    let alpha = alpha_max /. epsilon in
    
    
    (* D critère à maximiser *)
    let critere_D = alphad.d in
    let diff = max_D -. critere_D in
    
        (*Printf.printf "%f\n%!" diff;*)
        Printf.printf "%i %f %f %f %f %f\n%!" n max_D critere_D alpha norme_alpha moyenne_alpha;
    
    (* Stabilisation de la convergence *)

    let max_D =
        if diff > 0.
            then max_D
            else critere_D
    in
    (*
    if diff > 0. && epsilon > 0.001
        then localisation m_C methode (epsilon /. 1000.) (n-1) critere_D prev_m_alpha cc max_D
        else let epsilon = 1.
    in
    *)
        (*Util.debug_matrix "new_alpha_m" m_alpha;*)
        (*Util.debug_matrix "m_new_m_C" m_new_m_C;*)


    (* Critère de conv pour sortir *)
    if alpha_max**2.  < cc**2.
        then m_new_m_C
        else localisation m_new_m_C methode epsilon (n-1) critere_D m_alpha  cc max_D;;
In [ ]:
let localise localisation m_C methode epsilon n cc=
    let alphad = m_alpha_d methode m_C in
    let prev_m_alpha = alphad.m_alpha in 
    let prev_critere_D = 0. in
    let max_D = 0.
in
localisation m_C methode epsilon n prev_critere_D prev_m_alpha cc max_D;;
(*let new_mc = new_c m_C 0.001;;
let m_B = localise localisation new_mc "Boys" 1. 200 10e-10;;*)
In [ ]:
(*Fonction de création d'une list d'entier à partir d'un vecteur de float*)
let int_list vec = 
    let float_list = Vec.to_list vec
    in
    let g a = int_of_float(a)
in List.map g float_list;;

(* 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 list_int = int_list vec    
in
List.filter (fun x -> x > 0) 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);;

(* Liste des OMs occupées *)
let list_occ = 
    let vec_occ = Vec.init (nocc) (fun i -> float_of_int(i))
in int_list vec_occ;;

(* Liste des OMs de coeur *)
let list_core = 
    let vec_core = Vec.init (n_core) (fun i -> float_of_int(i))
in int_list vec_core;;

(* Fonction de rassemblement de 2 matrices *)
let assemble m_occ m_vir =
        let v_occ = Mat.to_col_vecs m_occ
        in
        let v_vir = Mat.to_col_vecs m_vir
        in
        let m = Array.append v_occ v_vir
in Mat.of_col_vecs m;;


(**********************************)
(*
m_C;;

let list_om = List.init nocc (fun i -> i+1)

let m_occ , m_vir = split_mat m_C list_om;;

assemble m_occ m_vir;;
*)
In [ ]:
let m_occ , m_vir = split_mat m_C list_occ;;
let dim_m_core = Mat.dim1 m_core;;
let m_core, m_occu = split_mat m_occ list_core;;
let new_core = 
    if dim_m_core = 0 
        then m_core 
        else new_c m_core 0.001;;
let new_occu = new_c m_occu 0.001;;
let new_vir = new_c m_vir 0.001;;
In [ ]:
let texte = "n max_D Critere_D alpha_max norme_alpha moyenne_alpha"
in Printf.printf "%s\n" texte;;

(*let new_m_boys = localise localisation m_C "boys"  1. 100  10e-7;;
let new_m_er = localise localisation m_C "ER"  1. 100  10e-7;;
*)
let loc_m_core_boys = 
    if dim_m_core=0 
        then m_core 
        else localise localisation m_core "boys_er" 1. 10 10e-3;;
let loc_m_occ_boys = localise localisation new_occ "boys_er" 1. 10 10e-3;;
let loc_m_vir_boys = localise localisation new_vir "BOYS" 1. 10 10e-3;;
(*let m_assemble_boys = assemble loc_m_occ_boys loc_m_vir_boys;;*)

(*
let loc_m_occ_er = localise localisation m_occ "ER"  1. 100  10e-7;;
let loc_m_vir_er = localise localisation m_vir "ER" 1. 100 10e-7;;
let m_assemble_er = assemble loc_m_occ_er loc_m_vir_er;;*)
(*
let loc_assemble_boys = localise localisation m_assemble_boys "boys" 1. 100  10e-7;;
let loc_assemble_er = localise localisation m_assemble_er "er" 1. 100 10e-7;;*)
In [ ]:
(*
let mo_base1 = MOBasis.make ~simulation ~mo_type:(MOBasis.Localized "Boys")
~mo_occupation:(MOBasis.mo_occupation mo_basis) ~mo_coef:new_m_boys ();;

let mo_base2 = MOBasis.make ~simulation ~mo_type:(MOBasis.Localized "ER")
~mo_occupation:(MOBasis.mo_occupation mo_basis) ~mo_coef:new_m_er ();;
*)

let mo_base3 = MOBasis.make ~simulation ~mo_type:(MOBasis.Localized "Boys")
~mo_occupation:(MOBasis.mo_occupation mo_basis) ~mo_coef:m_assemble_boys ();;

(*
let mo_base4 = MOBasis.make ~simulation ~mo_type:(MOBasis.Localized "ER")
~mo_occupation:(MOBasis.mo_occupation mo_basis) ~mo_coef:m_assemble_er ();;

let mo_base5 = MOBasis.make ~simulation ~mo_type:(MOBasis.Localized "Boys")
~mo_occupation:(MOBasis.mo_occupation mo_basis) ~mo_coef:loc_assemble_boys ();;

let mo_base6 = MOBasis.make ~simulation ~mo_type:(MOBasis.Localized "ER")
~mo_occupation:(MOBasis.mo_occupation mo_basis) ~mo_coef:loc_assemble_er ();;
*)
In [ ]:
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_base2 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"

*)
In [ ]:
(*
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
  
let hf = HartreeFock.make simulation

let mo_basis = MOBasis.of_hartree_fock hf
*)
In [ ]:
(*
let ci =
  DeterminantSpace.fci_of_mo_basis mo_basis ~frozen_core:false
  |> CI.make
  
let ci_coef, ci_energy = Lazy.force ci.eigensystem ;;
let m_ci_coef = Mat.as_vec ci_coef;;
Vec.iteri (fun i x -> Printf.printf "%e %f\n" i x) ci_coef;;
*)
In [ ]:

In [ ]:
let a = [1;2;3;4;5];;
List.iter print_int a;;
In [ ]:
let print_list f lst =
  let rec print_elements = function
    | [] -> ()
    | h::t -> f h; print_string ";"; print_elements t
  in
  print_string "[";
  print_elements lst;
  print_string "]";;
In [ ]:
print_list print_int [3;6;78;5;2;34;7];;
In [ ]:
let mat = mo_coef;;
      let n_ao = Mat.dim1 mat ;;
      let n_mo = Mat.dim2 mat ;;
      let m_pi = Mat.init_cols n_ao n_mo ( fun i j ->
          if mat.{i,j}**2. < 10e-8**2.
              then 0.
              else mat.{i,j})
    ;;
        Printf.printf "%i\n" nocc;;
        Util.debug_matrix "m_pi" m_pi;;
      let vec = Mat.to_col_vecs m_pi;;
      let vec_dot = Vec.init (n_mo-nocc) (fun i -> dot vec.(0) vec.(i-1+nocc))
      ;;
      let vec_pi = Vec.init (n_mo-nocc) (fun i ->
          if vec_dot.{i} = 0.
              then float_of_int(i)
              else 0.);;
      
      let list_pi = int_list vec_pi;;
      let rec remove x list =
          match list with
            | [] -> []
            | var :: tail -> if var = x
                       then remove x tail
                       else var :: (remove x tail);;
    remove 0 list_pi
In [ ]:

In [ ]:

In [ ]:

</html>