QCaml/MOBasis/MOBasis.ml

203 lines
5.5 KiB
OCaml
Raw Permalink Normal View History

2018-07-20 16:09:06 +02:00
open Lacaml.D
open Util
2018-08-05 00:31:48 +02:00
open Constants
2018-07-20 16:09:06 +02:00
(** One-electron orthogonal basis set, corresponding to Molecular Orbitals. *)
2019-03-01 21:56:46 +01:00
module HF = HartreeFock
2018-07-20 16:09:06 +02:00
module Si = Simulation
type mo_type =
| RHF | ROHF | UHF | CASSCF | Projected
2019-02-20 18:15:15 +01:00
| Natural of string
| Localized of string
2018-07-20 16:09:06 +02:00
2020-04-19 18:38:14 +02:00
type t =
2019-02-20 18:15:15 +01:00
{
simulation : Simulation.t; (* Simulation which produced the MOs *)
mo_type : mo_type; (* Kind of MOs (RHF, CASSCF, Localized...) *)
mo_occupation : Vec.t; (* Occupation numbers *)
mo_coef : Mat.t; (* Matrix of the MO coefficients in the AO basis *)
eN_ints : NucInt.t lazy_t; (* Electron-nucleus potential integrals *)
ee_ints : ERI.t lazy_t; (* Electron-electron potential integrals *)
2019-03-21 00:44:10 +01:00
f12_ints : F12.t lazy_t; (* F12 integrals *)
2019-02-20 18:15:15 +01:00
kin_ints : KinInt.t lazy_t; (* Kinetic energy integrals *)
2019-02-22 19:19:11 +01:00
one_e_ints : Mat.t lazy_t; (* Kinetic energy integrals *)
2019-02-20 18:15:15 +01:00
}
2018-07-20 16:09:06 +02:00
2019-02-19 17:36:07 +01:00
let size t =
2020-04-19 18:38:14 +02:00
Mat.dim2 t.mo_coef
2019-02-20 18:15:15 +01:00
let simulation t = t.simulation
let mo_type t = t.mo_type
let ao_basis t = Si.ao_basis t.simulation
let mo_occupation t = t.mo_occupation
let mo_coef t = t.mo_coef
let eN_ints t = Lazy.force t.eN_ints
let ee_ints t = Lazy.force t.ee_ints
let kin_ints t = Lazy.force t.kin_ints
2019-02-22 00:18:32 +01:00
let two_e_ints t = Lazy.force t.ee_ints
2019-03-21 00:44:10 +01:00
let f12_ints t = Lazy.force t.f12_ints
2019-02-22 19:19:11 +01:00
let one_e_ints t = Lazy.force t.one_e_ints
2018-07-20 16:09:06 +02:00
2019-11-14 10:58:57 +01:00
2020-04-19 18:38:14 +02:00
let mo_energies t =
2019-03-18 23:38:01 +01:00
let m_C = mo_coef t in
2020-04-19 18:38:14 +02:00
let f =
2019-03-01 21:56:46 +01:00
let m_N = Mat.of_diag @@ mo_occupation t in
2019-03-18 23:38:01 +01:00
let m_P = x_o_xt m_N m_C in
2019-03-01 21:56:46 +01:00
match t.mo_type with
| RHF -> Fock.make_rhf ~density:m_P (ao_basis t)
2019-11-20 16:42:28 +01:00
| Projected
2019-03-18 23:38:01 +01:00
| ROHF -> (Mat.scal 0.5 m_P;
Fock.make_uhf ~density_same:m_P ~density_other:m_P (ao_basis t))
2019-03-01 21:56:46 +01:00
| _ -> failwith "Not implemented"
in
2019-03-18 23:38:01 +01:00
let m_F0 = Fock.fock f in
xt_o_x m_F0 m_C
|> Mat.copy_diag
2019-03-01 21:56:46 +01:00
2020-04-19 18:38:14 +02:00
let mo_matrix_of_ao_matrix ~mo_coef ao_matrix =
2018-08-05 00:31:48 +02:00
xt_o_x ~x:mo_coef ~o:ao_matrix
2018-07-20 16:09:06 +02:00
let ao_matrix_of_mo_matrix ~mo_coef ~ao_overlap mo_matrix =
let sc = gemm ao_overlap mo_coef in
2019-02-26 11:58:53 +01:00
x_o_xt ~x:sc ~o:mo_matrix
2018-07-20 16:09:06 +02:00
2019-02-20 18:15:15 +01:00
let make ~simulation ~mo_type ~mo_occupation ~mo_coef () =
let ao_basis =
Si.ao_basis simulation
in
2018-07-20 16:09:06 +02:00
let eN_ints = lazy (
2019-02-20 18:24:44 +01:00
AOBasis.eN_ints ao_basis
2018-07-20 16:09:06 +02:00
|> NucInt.matrix
2019-03-21 00:44:10 +01:00
|> mo_matrix_of_ao_matrix ~mo_coef
2018-07-20 16:09:06 +02:00
|> NucInt.of_matrix
)
and kin_ints = lazy (
2019-02-20 18:24:44 +01:00
AOBasis.kin_ints ao_basis
2018-07-20 16:09:06 +02:00
|> KinInt.matrix
2020-04-19 18:38:14 +02:00
|> mo_matrix_of_ao_matrix ~mo_coef
2018-07-20 16:09:06 +02:00
|> KinInt.of_matrix
)
and ee_ints = lazy (
2019-02-20 18:24:44 +01:00
AOBasis.ee_ints ao_basis
2020-04-19 18:38:14 +02:00
|> ERI.four_index_transform mo_coef
2019-03-21 00:44:10 +01:00
)
and f12_ints = lazy (
AOBasis.f12_ints ao_basis
2020-04-19 18:38:14 +02:00
|> F12.four_index_transform mo_coef
2018-07-20 16:09:06 +02:00
)
in
2019-02-22 19:19:11 +01:00
let one_e_ints = lazy (
Mat.add (NucInt.matrix @@ Lazy.force eN_ints)
(KinInt.matrix @@ Lazy.force kin_ints) )
in
{ simulation ; mo_type ; mo_occupation ; mo_coef ;
2019-03-21 00:44:10 +01:00
eN_ints ; ee_ints ; kin_ints ; one_e_ints ;
f12_ints }
2018-07-20 16:09:06 +02:00
2020-04-19 18:38:14 +02:00
let values t point =
let c = mo_coef t in
let a = AOBasis.values (Simulation.ao_basis t.simulation) point in
gemv ~trans:`T c a
2019-02-26 12:47:23 +01:00
2020-04-19 18:38:14 +02:00
let of_hartree_fock hf =
2019-03-01 21:56:46 +01:00
let mo_coef = HF.eigenvectors hf in
let simulation = HF.simulation hf in
let mo_occupation = HF.occupation hf in
let mo_type =
match HF.kind hf with
| HartreeFock.RHF -> RHF
| HartreeFock.ROHF -> ROHF
| HartreeFock.UHF -> UHF
in
2019-02-26 12:47:23 +01:00
make ~simulation ~mo_type ~mo_occupation ~mo_coef ()
2018-07-20 16:09:06 +02:00
let of_mo_basis simulation other =
2020-04-19 18:38:14 +02:00
let mo_coef =
let basis = Simulation.ao_basis simulation in
let basis_other = ao_basis other in
let m_S =
Overlap.(matrix @@ of_basis_pair
(AOBasis.basis basis)
(AOBasis.basis basis_other) )
in
let m_X = AOBasis.ortho basis in
(* Project other vectors in the current basis *)
let m_C =
gemm m_S @@ mo_coef other
in
(* Append dummy vectors to the input vectors *)
let result =
let vecs = Mat.to_col_vecs m_X in
Array.iteri (fun i v -> if (i < Array.length vecs) then vecs.(i) <- v)
(Mat.to_col_vecs m_C) ;
Mat.of_col_vecs vecs
in
(* Gram-Schmidt Orthonormalization *)
gemm m_X @@ (Util.qr_ortho @@ gemm ~transa:`T m_X result)
2019-11-20 16:42:28 +01:00
|> Util.remove_epsilons
2019-11-14 10:58:57 +01:00
|> Conventions.rephase
in
let mo_occupation =
let occ = mo_occupation other in
Vec.init (Mat.dim2 mo_coef) (fun i ->
if (i <= Vec.dim occ) then occ.{i}
else 0.)
in
make ~simulation ~mo_type:Projected ~mo_occupation ~mo_coef ()
2020-04-19 18:38:14 +02:00
2019-12-03 09:13:57 +01:00
let pp ?(start=1) ?(finish=0) ppf t =
2019-03-01 21:56:46 +01:00
let open Lacaml.Io in
let rows = Mat.dim1 t.mo_coef
and cols = Mat.dim2 t.mo_coef
in
let finish =
match finish with
2019-12-03 09:13:57 +01:00
| 0 -> cols
| x -> x
2019-03-01 21:56:46 +01:00
in
2020-04-19 18:38:14 +02:00
let rec aux first =
2019-03-01 21:56:46 +01:00
if (first > finish) then ()
else
begin
Format.fprintf ppf "@[<v>@[<v4>@[<h>%s@;" "Eigenvalues:";
Array.iteri (fun i x ->
if (i+1 >= first) && (i+1 <= first+4 ) then
Format.fprintf ppf "%12f@ " x)
(Vec.to_array @@ mo_energies t);
Format.fprintf ppf "@]@;";
Format.fprintf ppf "@[%a@]"
(Lacaml.Io.pp_lfmat
~row_labels:
(Array.init rows (fun i -> Printf.sprintf "%d " (i + 1)))
~col_labels:
(Array.init (min 5 (cols-first+1)) (fun i -> Printf.sprintf "-- %d --" (i + first) ))
2020-04-19 18:38:14 +02:00
~print_right:false
2019-03-01 21:56:46 +01:00
~print_foot:false
() ) (lacpy ~ac:first ~n:(min 5 (cols-first+1)) (t.mo_coef)) ;
Format.fprintf ppf "@]@;@;@]";
2020-04-19 18:38:14 +02:00
(aux [@tailcall]) (first+5)
2019-03-01 21:56:46 +01:00
end
in
2020-04-19 18:38:14 +02:00
aux start
2018-07-20 16:09:06 +02:00
2019-02-19 17:36:07 +01:00