10
1
mirror of https://gitlab.com/scemama/QCaml.git synced 2024-06-02 11:25:19 +02:00
QCaml/CI/F12CI.ml

346 lines
8.9 KiB
OCaml
Raw Normal View History

2019-03-21 00:44:10 +01:00
open Lacaml.D
type t =
{
mo_basis : MOBasis.t ;
aux_basis : MOBasis.t ;
det_space : DeterminantSpace.t ;
ci : CI.t ;
2019-03-21 21:48:21 +01:00
eigensystem : (Mat.t * Vec.t) lazy_t;
2019-03-21 00:44:10 +01:00
}
let ci t = t.ci
let mo_basis t = t.mo_basis
let det_space t = t.det_space
let mo_class t = DeterminantSpace.mo_class @@ det_space t
2019-03-22 00:34:00 +01:00
let eigensystem t = Lazy.force t.eigensystem
2019-03-21 00:44:10 +01:00
2019-03-21 11:02:58 +01:00
2019-03-21 00:44:10 +01:00
let f12_integrals mo_basis =
2019-03-22 18:16:41 +01:00
let two_e_ints = MOBasis.f12_ints mo_basis in
2019-03-25 09:17:13 +01:00
( (fun _ _ _ -> 0.),
2019-03-21 00:44:10 +01:00
(fun i j k l s s' ->
2019-03-28 09:37:43 +01:00
if (i=k && j<>l) || (j=l && i<>k) then
0.
2019-03-21 00:44:10 +01:00
else
2019-03-28 09:37:43 +01:00
begin
2019-05-07 17:00:44 +02:00
let ijkl = F12.get_phys two_e_ints i j k l
2019-05-27 17:35:28 +02:00
in
let ijlk = F12.get_phys two_e_ints i j l k
2019-05-07 17:00:44 +02:00
in
2019-03-28 09:37:43 +01:00
if s' = Spin.other s then
2019-05-09 10:49:53 +02:00
(* Minus sign because we swap spin variables
instead of orbital variables *)
2019-05-29 16:43:44 +02:00
0.375 *. ijkl +. 0.125 *. ijlk
2019-03-28 09:37:43 +01:00
else
2019-05-07 17:00:44 +02:00
0.25 *. (ijkl -. ijlk)
2019-03-28 09:37:43 +01:00
end
2019-03-21 00:44:10 +01:00
) )
2019-03-21 11:02:58 +01:00
2019-03-21 00:44:10 +01:00
let h_ij mo_basis ki kj =
let integrals =
List.map (fun f -> f mo_basis)
[ CI.h_integrals ]
in
CIMatrixElement.make integrals ki kj
|> List.hd
2019-03-25 15:20:01 +01:00
let f_ij mo_basis ki kj =
2019-03-21 00:44:10 +01:00
let integrals =
List.map (fun f -> f mo_basis)
[ f12_integrals ]
in
2019-05-29 16:43:44 +02:00
CIMatrixElement.make integrals ki kj
2019-03-25 15:20:01 +01:00
|> List.hd
2019-03-21 00:44:10 +01:00
2019-05-27 17:35:28 +02:00
let hf_ij mo_basis ki kj =
2019-08-05 13:24:41 +02:00
(*
2019-05-27 17:35:28 +02:00
[ h_ij mo_basis ki kj ; f_ij mo_basis ki kj ]
2019-08-05 13:24:41 +02:00
*)
let integrals =
List.map (fun f -> f mo_basis)
[ CI.h_integrals ; f12_integrals ]
in
CIMatrixElement.make integrals ki kj
2019-05-27 17:35:28 +02:00
2019-03-21 00:44:10 +01:00
2019-05-29 09:04:43 +02:00
let is_a_double det_space =
2019-03-26 01:20:17 +01:00
let mo_class = DeterminantSpace.mo_class det_space in
let mo_num = Array.length @@ MOClass.mo_class_array mo_class in
2019-03-22 18:16:41 +01:00
let m l =
List.fold_left (fun accu i ->
2019-03-26 01:20:17 +01:00
let j = i-1 in Bitstring.logor accu (Bitstring.shift_left_one mo_num j)
) (Bitstring.zero mo_num) l
2019-03-22 18:16:41 +01:00
in
2019-03-26 01:20:17 +01:00
let aux_mask = m (MOClass.auxiliary_mos mo_class) in
2019-03-22 18:16:41 +01:00
fun a ->
let alfa =
Determinant.alfa a
|> Spindeterminant.bitstring
in
2019-03-26 01:20:17 +01:00
let beta =
Determinant.beta a
|> Spindeterminant.bitstring
in
let a = Bitstring.logand aux_mask alfa
and b = Bitstring.logand aux_mask beta
2019-03-28 09:37:43 +01:00
in
match Bitstring.popcount a + Bitstring.popcount b with
2019-05-29 09:04:43 +02:00
| 2 -> true
| _ -> false
2019-03-26 01:20:17 +01:00
2019-03-22 18:16:41 +01:00
2019-03-26 10:38:50 +01:00
let dressing_vector ~frozen_core aux_basis f12_amplitudes ci =
2019-03-21 00:44:10 +01:00
2019-03-29 19:01:51 +01:00
if Parallel.master then
Printf.printf "Building matrix\n%!";
2019-03-25 10:54:23 +01:00
2019-03-28 09:37:43 +01:00
(* Determinants of the FCI space as a list *)
let in_dets =
2019-03-29 19:01:51 +01:00
DeterminantSpace.determinant_stream ci.CI.det_space
|> Util.stream_to_list
2019-03-28 09:37:43 +01:00
in
2019-03-25 16:50:36 +01:00
2019-03-28 09:37:43 +01:00
(* Stream that generates only singly and doubly excited determinants
wrt FCI space *)
let out_dets_stream =
2019-03-29 19:01:51 +01:00
(* Stream that generates all determinants of FCI space *)
let s =
DeterminantSpace.fci_of_mo_basis ~frozen_core aux_basis
|> DeterminantSpace.determinant_stream
2019-03-28 09:37:43 +01:00
2019-03-29 19:01:51 +01:00
in
2019-05-29 09:04:43 +02:00
(* Select only doubly excited determinants wrt FCI space *)
2019-03-29 19:01:51 +01:00
Stream.from (fun _ ->
try
let rec result () =
let ki = Stream.next s in
2019-05-29 09:04:43 +02:00
if not (is_a_double ci.CI.det_space ki) then
2019-03-29 19:01:51 +01:00
result ()
else
Some ki
in
result ()
with Stream.Failure -> None
)
2019-03-28 09:37:43 +01:00
in
2019-03-25 16:50:36 +01:00
2019-03-28 10:32:54 +01:00
let make_h_and_f alpha_list =
2019-03-28 09:37:43 +01:00
let rec col_vecs_list accu_H accu_F = function
2019-03-28 10:32:54 +01:00
| [] ->
2019-03-28 09:37:43 +01:00
List.rev accu_H,
List.rev accu_F
2019-03-28 10:32:54 +01:00
| ki :: rest ->
2019-03-28 09:37:43 +01:00
let h, f =
List.map (fun kj ->
match hf_ij aux_basis ki kj with
| [ a ; b ] -> a, b
| _ -> assert false ) in_dets
|> List.split
in
let h =
Vec.of_list h
and f =
Vec.of_list f
in
2019-03-28 10:32:54 +01:00
col_vecs_list (h::accu_H) (f::accu_F) rest
2019-03-25 09:17:13 +01:00
in
let h, f =
2019-03-28 10:32:54 +01:00
col_vecs_list [] [] alpha_list
2019-03-25 09:17:13 +01:00
in
2019-03-28 09:37:43 +01:00
Mat.of_col_vecs_list h,
2019-03-25 09:17:13 +01:00
Mat.of_col_vecs_list f
2019-03-22 18:16:41 +01:00
in
let m_HF =
2019-03-29 19:01:51 +01:00
let batch_size = 1 + 1_000_000 / (Mat.dim1 f12_amplitudes) in
2019-03-28 10:32:54 +01:00
let input_stream =
Stream.from (fun i ->
let rec make_batch accu = function
| 0 -> accu
| n -> try
let alpha = Stream.next out_dets_stream in
let accu = alpha :: accu in
make_batch accu (n-1)
with Stream.Failure -> accu
in
let result = make_batch [] batch_size in
if result = [] then None else Some result
)
in
let iteration input =
2019-03-29 19:01:51 +01:00
Printf.printf ".%!";
2019-03-28 10:32:54 +01:00
let m_H_aux, m_F_aux = make_h_and_f input in
let m_HF =
2019-03-28 09:37:43 +01:00
gemm m_H_aux m_F_aux ~transb:`T
in
2019-03-28 10:32:54 +01:00
gemm m_HF f12_amplitudes
in
2019-05-27 17:35:28 +02:00
let result =
let x =
try [ Stream.next out_dets_stream ]
with Stream.Failure -> failwith "Auxiliary basis set does not produce any excited determinant"
in
iteration x
in
2019-03-28 10:32:54 +01:00
input_stream
|> Farm.run ~ordered:false ~f:iteration
|> Stream.iter (fun hf ->
ignore @@ Mat.add result hf ~c:result );
2019-03-29 19:01:51 +01:00
Printf.printf "\n";
Parallel.broadcast (lazy result)
2019-03-25 19:28:38 +01:00
in
2019-04-05 18:05:56 +02:00
if Parallel.master then Printf.printf "Done\n%!";
2019-03-28 10:32:54 +01:00
Matrix.dense_of_mat m_HF
2019-03-21 00:44:10 +01:00
2019-05-07 17:00:44 +02:00
let make ~simulation ?(threshold=1.e-12) ~frozen_core ~mo_basis ~aux_basis_filename ?(state=1) () =
2019-03-21 00:44:10 +01:00
2019-03-26 10:38:50 +01:00
let f12 = Util.of_some @@ Simulation.f12 simulation in
2019-03-21 00:44:10 +01:00
let mo_num = MOBasis.size mo_basis in
2019-03-28 09:37:43 +01:00
Printf.printf "Add aux basis\n%!";
2019-03-21 00:44:10 +01:00
(* Add auxiliary basis set *)
let s =
let charge = Charge.to_int @@ Simulation.charge simulation
and multiplicity = Electrons.multiplicity @@ Simulation.electrons simulation
and nuclei = Simulation.nuclei simulation
in
let general_basis =
2019-03-21 21:48:21 +01:00
Basis.general_basis @@ Simulation.basis simulation
2019-03-21 00:44:10 +01:00
in
GeneralBasis.combine [
2019-03-21 21:48:21 +01:00
general_basis ; GeneralBasis.read aux_basis_filename
]
2019-03-21 00:44:10 +01:00
|> Basis.of_nuclei_and_general_basis nuclei
2019-03-26 10:38:50 +01:00
|> Simulation.make ~f12 ~charge ~multiplicity ~nuclei
2019-03-21 00:44:10 +01:00
in
let aux_basis =
MOBasis.of_mo_basis s mo_basis
in
2019-03-28 09:37:43 +01:00
let () =
ignore @@ MOBasis.f12_ints aux_basis
in
let () =
ignore @@ MOBasis.two_e_ints aux_basis
in
2019-03-21 00:44:10 +01:00
let det_space =
2019-03-21 21:48:21 +01:00
DeterminantSpace.fci_f12_of_mo_basis aux_basis ~frozen_core mo_num
2019-03-21 00:44:10 +01:00
in
2019-05-07 17:00:44 +02:00
let ci = CI.make ~n_states:state det_space in
2019-03-21 11:02:58 +01:00
2019-03-22 00:34:00 +01:00
let ci_coef, ci_energy =
let x = Lazy.force ci.eigensystem in
Parallel.broadcast (lazy x)
in
2019-03-21 11:02:58 +01:00
2019-03-22 00:34:00 +01:00
let e_shift =
2019-03-21 21:48:21 +01:00
let det =
DeterminantSpace.determinant_stream det_space
|> Stream.next
in
2019-03-22 00:34:00 +01:00
h_ij aux_basis det det
2019-03-21 21:48:21 +01:00
in
2019-03-21 11:02:58 +01:00
2019-03-21 21:48:21 +01:00
let eigensystem = lazy (
let m_H =
Lazy.force ci.CI.m_H
in
2019-05-07 17:00:44 +02:00
let rec iteration ~state psi =
let column_idx = iamax (Mat.to_col_vecs psi).(state-1) in
2019-03-22 00:34:00 +01:00
let delta =
2019-05-27 17:35:28 +02:00
(* delta_i = {% $\sum_j c_j H_{ij}$ %} *)
2019-03-28 09:37:43 +01:00
dressing_vector ~frozen_core aux_basis psi ci
2019-05-27 17:35:28 +02:00
|> Matrix.to_mat
2019-03-22 00:34:00 +01:00
in
2019-05-29 09:04:43 +02:00
Printf.printf "Cmax : %e\n" psi.{column_idx,state};
Printf.printf "Norm : %e\n" (sqrt (gemm ~transa:`T delta delta).{state,state});
2019-05-07 17:00:44 +02:00
let f = 1.0 /. psi.{column_idx,state} in
2019-03-22 22:42:20 +01:00
let delta_00 =
2019-05-27 17:35:28 +02:00
(* Delta_00 = {% $\sum_{j \ne x} delta_j c_j / c_x$ %} *)
f *. ( (gemm ~transa:`T delta psi).{state,state} -.
delta.{column_idx,state} *. psi.{column_idx,state} )
2019-03-22 22:42:20 +01:00
in
2019-05-29 09:04:43 +02:00
Printf.printf "Delta_00 : %e %e\n" delta.{column_idx,state} delta_00;
2019-05-07 17:00:44 +02:00
delta.{column_idx,state} <- delta.{column_idx,state} -. delta_00;
2019-03-22 22:42:20 +01:00
2019-03-21 21:48:21 +01:00
let diagonal =
2019-03-22 23:42:35 +01:00
Vec.init (Matrix.dim1 m_H) (fun i ->
2019-05-07 17:00:44 +02:00
if i = column_idx then
Matrix.get m_H i i +. delta.{column_idx,state} *. f
2019-03-22 23:42:35 +01:00
else
Matrix.get m_H i i
)
2019-03-21 21:48:21 +01:00
in
2019-03-23 01:06:38 +01:00
2019-03-22 00:34:00 +01:00
let matrix_prod c =
2019-03-22 22:42:20 +01:00
let w =
Matrix.mm ~transa:`T m_H c
|> Matrix.to_mat
in
2019-05-27 17:35:28 +02:00
let c11 = Matrix.get c column_idx state in
2019-03-22 22:42:20 +01:00
Util.list_range 1 (Mat.dim1 w)
|> List.iter (fun i ->
2019-03-22 23:42:35 +01:00
let dci =
2019-05-07 17:00:44 +02:00
delta.{i,state} *. f ;
2019-03-22 23:42:35 +01:00
in
2019-05-07 17:00:44 +02:00
w.{i,state} <- w.{i,state} +. dci *. c11;
if (i <> column_idx) then
w.{column_idx,state} <- w.{column_idx,state} +. dci *. (Matrix.get c i state);
2019-03-22 22:42:20 +01:00
);
Matrix.dense_of_mat w
2019-03-21 21:48:21 +01:00
in
2019-03-23 01:06:38 +01:00
2019-03-21 21:48:21 +01:00
let eigenvectors, eigenvalues =
Parallel.broadcast (lazy (
2019-05-07 17:00:44 +02:00
Davidson.make ~threshold:1.e-6 ~guess:psi ~n_states:state diagonal matrix_prod
2019-03-21 21:48:21 +01:00
))
in
2019-05-07 17:00:44 +02:00
Vec.iter (fun energy -> Printf.printf "%g\t" energy) eigenvalues;
print_newline ();
2019-03-23 01:06:38 +01:00
2019-03-22 19:15:59 +01:00
let conv =
1.0 -. abs_float ( dot
(Mat.to_col_vecs psi).(0)
(Mat.to_col_vecs eigenvectors).(0) )
2019-03-21 21:48:21 +01:00
in
2019-03-23 14:54:59 +01:00
if Parallel.master then
2019-05-07 17:00:44 +02:00
Printf.printf "F12 Convergence : %e %f\n" conv (eigenvalues.{state} +. e_shift
2019-03-25 16:50:36 +01:00
+. Simulation.nuclear_repulsion simulation);
2019-03-23 01:06:38 +01:00
2019-03-21 21:48:21 +01:00
if conv > threshold then
2019-05-07 17:00:44 +02:00
iteration ~state eigenvectors
2019-03-21 21:48:21 +01:00
else
let eigenvalues =
Vec.map (fun x -> x +. e_shift) eigenvalues
in
eigenvectors, eigenvalues
in
2019-05-07 17:00:44 +02:00
iteration ~state ci_coef
2019-03-22 00:34:00 +01:00
2019-03-21 21:48:21 +01:00
)
in
2019-03-26 10:38:50 +01:00
{ mo_basis ; aux_basis ; det_space ; ci ; eigensystem }
2019-03-21 00:44:10 +01:00