#+begin_src elisp tangle: no :results none :exports none (setq pwd (file-name-directory buffer-file-name)) (setq name (file-name-nondirectory (substring buffer-file-name 0 -4))) (setq lib (concat pwd "lib/")) (setq testdir (concat pwd "test/")) (setq mli (concat lib name ".mli")) (setq ml (concat lib name ".ml")) (setq test-ml (concat testdir name ".ml")) (org-babel-tangle) #+end_src * Orbital localization :PROPERTIES: :header-args: :noweb yes :comments both :END: Molecular orbital localization function. Boys: Edmiston-Rudenberg: ** Type #+NAME: types #+begin_src ocaml :tangle (eval mli) open Linear_algebra type localization_kind = | Edmiston | Boys type mo = Mo_dim.t type ao = Ao.Ao_dim.t type loc #+end_src #+begin_src ocaml :tangle (eval mli) type localization_data type t #+end_src #+begin_src ocaml :tangle (eval ml) :exports none <> type localization_data = { coefficients : (ao, loc) Matrix.t ; kappa : (loc, loc) Matrix.t ; scaling : float ; loc_value : float ; convergence : float ; iteration : int ; } type t = { kind : localization_kind ; mo_basis : Basis.t ; data : localization_data option lazy_t array ; selected_mos : int list ; } open Common #+end_src ** Edmiston-Rudenberg #+begin_src ocaml :tangle (eval ml) :exports none let kappa_edmiston in_basis m_C = let ao_basis = Basis.simulation in_basis |> Simulation.ao_basis in let ee_ints = Ao.Basis.ee_ints ao_basis in let n_ao = Ao.Basis.size ao_basis in let n_mo = Matrix.dim2 m_C in (* Temp arrays for integral transformation *) let m_pqr = Bigarray.(Array3.create Float64 fortran_layout n_ao n_ao n_ao) in let m_qr_i = Matrix.create (n_ao*n_ao) n_mo in let m_ri_j = Matrix.create (n_ao*n_mo) n_mo in let m_ij_k = Matrix.create (n_mo*n_mo) n_mo in let m_a12 = Bigarray.(Array2.create Float64 fortran_layout n_mo n_mo) in let m_b12 = Bigarray.(Array2.create Float64 fortran_layout n_mo n_mo) in Bigarray.Array2.fill m_b12 0.; Bigarray.Array2.fill m_a12 0.; let v_d = Vector.init n_mo (fun _ -> 0.) |> Vector.to_bigarray_inplace in Array.iter (fun s -> Array.iter (fun r -> Array.iter (fun q -> Array.iter (fun p -> m_pqr.{p,q,r} <- Four_idx_storage.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); let m_p_qr = Bigarray.reshape (Bigarray.genarray_of_array3 m_pqr) [| n_ao ; n_ao*n_ao |] |> Bigarray.array2_of_genarray |> Matrix.of_bigarray_inplace in (* (qr,i) = = \sum_p

C_{pi} *) Matrix.gemm_tn_inplace ~c:m_qr_i m_p_qr m_C; let m_q_ri = let x = Matrix.to_bigarray_inplace m_qr_i |> Bigarray.genarray_of_array2 in Bigarray.reshape_2 x n_ao (n_ao*n_mo) |> Matrix.of_bigarray_inplace in (* (ri,j) = = \sum_q C_{qj} *) Matrix.gemm_tn_inplace ~c:m_ri_j m_q_ri m_C; let m_r_ij = let x = Matrix.to_bigarray_inplace m_ri_j |> Bigarray.genarray_of_array2 in Bigarray.reshape_2 x n_ao (n_mo*n_mo) |> Matrix.of_bigarray_inplace in (* (ij,k) = = \sum_r C_{rk} *) Matrix.gemm_tn_inplace ~c:m_ij_k m_r_ij m_C; let m_ijk = let x = Matrix.to_bigarray_inplace m_ij_k |> Bigarray.genarray_of_array2 in Bigarray.reshape x [| n_mo ; n_mo ; n_mo |] |> Bigarray.array3_of_genarray in let m_Cx = Matrix.to_bigarray_inplace m_C in Array.iter (fun j -> Array.iter (fun i -> m_b12.{i,j} <- m_b12.{i,j} +. m_Cx.{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_Cx.{s,j} -. 0.25 *. ( (m_ijk.{i,i,i} -. m_ijk.{j,i,j}) *. m_Cx.{s,i} +. (m_ijk.{j,j,j} -. m_ijk.{i,j,i}) *. m_Cx.{s,j}) ) (Util.array_range 1 n_mo); v_d.{j} <- v_d.{j} +. m_ijk.{j,j,j} *. m_Cx.{s,j} ) (Util.array_range 1 n_mo) ) (Util.array_range 1 n_ao); let f i j = if i=j then 0. else begin let x = 1./. sqrt (m_b12.{i,j} *. m_b12.{i,j} +. m_a12.{i,j} *. m_a12.{i,j} ) in if asin (m_b12.{i,j} /. x) > 0. then 0.25 *. acos( -. m_a12.{i,j} *. x) else -. 0.25 *. acos( -. m_a12.{i,j} *. x ) end in ( Matrix.init_cols n_mo n_mo ( fun i j -> if i<=j then f i j else -. (f j i) ), Vector.sum (Vector.of_bigarray_inplace v_d) ) #+end_src ** Boys #+begin_src ocaml :tangle (eval ml) :exports none let kappa_boys in_basis = let ao_basis = Basis.simulation in_basis |> Simulation.ao_basis in let multipole = Ao.Basis.multipole ao_basis in fun m_C -> let n_mo = Matrix.dim2 m_C in let phi_x_phi = Matrix.xt_o_x ~x:m_C ~o:(multipole "x") in let phi_y_phi = Matrix.xt_o_x ~x:m_C ~o:(multipole "y") in let phi_z_phi = Matrix.xt_o_x ~x:m_C ~o:(multipole "z") in let m_b12 = let g x i j = let x_ii = x%:(i,i) in let x_jj = x%:(j,j) in let x_ij = x%:(i,j) in (x_ii -. x_jj) *. x_ij in Matrix.init_cols n_mo n_mo (fun i j -> let x = (g phi_x_phi i j) +. (g phi_y_phi i j) +. (g phi_z_phi i j) in if (abs_float x > 1.e-15) then x else 0. ) in let m_a12 = let g x i j = let x_ii = x%:(i,i) in let x_jj = x%:(j,j) in let x_ij = x%:(i,j) in let y = x_ii -. x_jj in (x_ij *. x_ij) -. 0.25 *. y *. y in Matrix.init_cols n_mo n_mo (fun i j -> let x = (g phi_x_phi i j) +. (g phi_y_phi i j) +. (g phi_z_phi i j) in if (abs_float x > 1.e-15) then x else 0. ) in let f i j = let pi = Constants.pi in if i=j then 0. else let x = atan2 (m_b12%:(i,j)) (m_a12%:(i,j)) in if x >= 0. then 0.25 *. (pi -. x) else -. 0.25 *. ( pi +. x) in ( Matrix.init_cols n_mo n_mo ( fun i j -> if i>j then f i j else -. (f j i) ), let r2 x y z = x*.x +. y*.y +. z*.z in Vector.init n_mo ( fun i -> r2 (phi_x_phi%:(i,i)) (phi_y_phi%:(i,i)) (phi_z_phi%:(i,i))) |> Vector.sum ) #+end_src ** Access #+begin_src ocaml :tangle (eval mli) val kind : t -> localization_kind val simulation : t -> Simulation.t val selected_mos : t -> int list val kappa : kind:localization_kind -> Basis.t -> ( ao,loc) Matrix.t -> (loc,loc) Matrix.t * float val make : kind:localization_kind -> ?max_iter:int -> ?convergence:float -> Basis.t -> int list -> t val to_basis : t -> Basis.t #+end_src | ~kappa~ | Returns the $\kappa$ antisymmetric matrix used for the rotation matrix and the value of the localization function | | ~make~ | Performs the orbital localization | #+begin_src ocaml :tangle (eval ml) :exports none let kind t = t.kind let simulation t = Basis.simulation t.mo_basis let selected_mos t = t.selected_mos let kappa ~kind = match kind with | Edmiston -> kappa_edmiston | Boys -> kappa_boys let n_iterations t = Array.fold_left (fun accu x -> match Lazy.force x with | Some _ -> accu + 1 | None -> accu ) 0 t.data let last_iteration t = Util.of_some @@ Lazy.force t.data.(n_iterations t - 1) (* let ao_basis t = Simulation.ao_basis (simulation t) ,*) let make ~kind ?(max_iter=500) ?(convergence=1.e-6) mo_basis selected_mos = let kappa_loc = kappa ~kind mo_basis in let mo_array = Matrix.to_col_vecs (Basis.mo_coef mo_basis) in let mos_vec_list = List.map (fun i -> mo_array.(i-1)) selected_mos in let x: (ao,loc) Matrix.t = Matrix.of_col_vecs_list mos_vec_list in let next_coef kappa x = let r = Matrix.exponential_antisymmetric kappa in let m_C = Matrix.gemm_nt x r in m_C in let data_initial = let iteration = 0 and scaling = 0.1 in let kappa, loc_value = kappa_loc x in let convergence = abs_float (Matrix.amax kappa) in let kappa = Matrix.scale scaling kappa in let coefficients = next_coef kappa x in { coefficients ; kappa ; scaling ; convergence ; loc_value ; iteration } in let iteration data = let iteration = data.iteration+1 in let new_kappa, new_loc_value = kappa_loc data.coefficients in let new_convergence = abs_float (Matrix.amax new_kappa) in let accept = new_loc_value >= data.loc_value*.0.98 in if accept then let coefficients = next_coef new_kappa data.coefficients in let scaling = min 1. (data.scaling *. 1.1) in let kappa = Matrix.scale scaling new_kappa in let convergence = new_convergence in let loc_value = new_loc_value in { coefficients ; kappa ; scaling ; convergence ; loc_value ; iteration } else let scaling = data.scaling *. 0.5 in { data with scaling ; iteration } in let array_data = let storage = Array.make max_iter None in let rec loop = function | 0 -> Some (iteration data_initial) | n -> begin match storage.(n) with | Some result -> Some result | None -> begin let data = loop (n-1) in match data with | None -> None | Some data -> begin (* Check convergence *) let converged = data.convergence < convergence in if converged then None else begin storage.(n-1) <- Some data ; storage.(n) <- Some (iteration data); storage.(n) end end end end in Array.init max_iter (fun i -> lazy (loop i)) in { kind ; mo_basis ; data = array_data ; selected_mos } let to_basis t = let mo_basis = t.mo_basis in let simulation = Basis.simulation mo_basis in let mo_occupation = Basis.mo_occupation mo_basis in let data = last_iteration t in let mo_coef_array = Matrix.to_col_vecs (Basis.mo_coef mo_basis) in let new_mos = Matrix.to_col_vecs data.coefficients in selected_mos t |> List.iteri (fun i j -> mo_coef_array.(j-1) <- new_mos.(i)) ; let mo_coef = Matrix.of_col_vecs mo_coef_array in Basis.make ~simulation ~mo_type:(Localized "Boys") ~mo_occupation ~mo_coef () #+end_src ** Printers #+begin_src ocaml :tangle (eval mli) val pp : Format.formatter -> t -> unit #+end_src #+begin_src ocaml :tangle (eval ml) :exports none let linewidth = 60 let pp_iterations ppf t = let line = (String.make linewidth '-') in Format.fprintf ppf "@[%4s%s@]@." "" line; Format.fprintf ppf "@[%4s@[%5s@]@,@[%16s@]@,@[%16s@]@,@[%11s@]@]@." "" "#" "Localization " "Convergence" "Scaling"; Format.fprintf ppf "@[%4s%s@]@." "" line; Array.iter (fun data -> let data = Lazy.force data in match data with | None -> () | Some data -> let loc = data.loc_value in let conv = data.convergence in let scaling = data.scaling in let iteration = data.iteration in begin Format.fprintf ppf "@[%4s@[%5d@]@,@[%16.8f@]@,@[%16.4e@]@,@[%11.4f@]@]@." "" iteration loc conv scaling ; end ) t.data; Format.fprintf ppf "@[%4s%s@]@." "" line let pp ppf t = Format.fprintf ppf "@.@[%s@]@." (String.make 70 '='); Format.fprintf ppf "@[%34s %-34s@]@." (match t.kind with | Boys -> "Boys" | Edmiston -> "Edmiston-Ruedenberg" ) "MO Localization"; Format.fprintf ppf "@[%s@]@.@." (String.make 70 '='); Format.fprintf ppf "@[%a@]@." pp_iterations t; #+end_src ** Tests #+begin_src ocaml :tangle (eval test-ml) :exports none let test_localization = let nuclei = Particles.Nuclei.of_xyz_string " 10 Hydrogen chain, d=1.8 Angstrom H -4.286335 0.000000 0.000000 H -3.333816 0.000000 0.000000 H -2.381297 0.000000 0.000000 H -1.428778 0.000000 0.000000 H -0.476259 0.000000 0.000000 H 0.476259 0.000000 0.000000 H 1.428778 0.000000 0.000000 H 2.381297 0.000000 0.000000 H 3.333816 0.000000 0.000000 H 4.286335 0.000000 0.000000 " in let basis_file = "/home/scemama/qp2/data/basis/sto-6g" in let ao_basis = Ao.Basis.of_nuclei_and_basis_filename ~nuclei basis_file in let charge = 0 in let multiplicity = 1 in let simulation = Simulation.make ~charge ~multiplicity ~nuclei ao_basis in let hf = Mo.Hartree_fock.make ~guess:`Hcore simulation in let mo_basis = Mo.Basis.of_hartree_fock hf in let localized_mo_basis = Mo.Localization.make ~kind:Mo.Localization.Boys mo_basis [4;5;6;7;8] |> Mo.Localization.to_basis in Format.printf "%a" (Mo.Basis.pp ~start:1 ~finish:10) localized_mo_basis (* open Common open Alcotest let wd = Qcaml.root ^ Filename.dir_sep ^ "test" in let test_xyz molecule length repulsion charge core = let xyz = Nuclei.of_xyz_file (wd^Filename.dir_sep^molecule^".xyz") in check int "length" length (Array.length xyz); check (float 1.e-4) "repulsion" repulsion (Nuclei.repulsion xyz); check int "charge" charge (Charge.to_int @@ Nuclei.charge xyz); check int "small_core" core (Nuclei.small_core xyz); () let tests = [ "caffeine", `Quick, (fun () -> test_xyz "caffeine" 24 917.0684 102 28); "water", `Quick, (fun () -> test_xyz "water" 3 9.19497 10 2); ] ,*) #+end_src