10
1
mirror of https://gitlab.com/scemama/QCaml.git synced 2024-10-15 04:21:38 +02:00
QCaml/SCF/RHF.ml

292 lines
6.6 KiB
OCaml
Raw Normal View History

2018-02-23 18:44:31 +01:00
open Util
open Constants
2018-02-23 18:44:31 +01:00
open Lacaml.D
2018-06-13 17:49:58 +02:00
module Si = Simulation
module El = Electrons
module Ao = AOBasis
2018-06-13 19:03:42 +02:00
module Ov = Overlap
2018-02-23 18:44:31 +01:00
2019-03-01 10:30:02 +01:00
type hartree_fock_data =
{
iteration : int ;
coefficients : Mat.t option ;
eigenvalues : Vec.t option ;
error : float option ;
diis : DIIS.t option ;
energy : float option ;
density : Mat.t option ;
fock : Fock.t option ;
}
let empty =
{
iteration = 0 ;
coefficients = None ;
eigenvalues = None ;
error = None ;
diis = None ;
energy = None ;
density = None ;
fock = None ;
}
2019-02-27 21:28:56 +01:00
let make ~guess ~max_scf ~level_shift ~threshold_SCF simulation =
2018-02-23 18:44:31 +01:00
(* Number of occupied MOs *)
let nocc =
2019-02-20 19:43:16 +01:00
El.n_alfa @@ Si.electrons simulation
2018-02-23 18:44:31 +01:00
in
2018-05-30 18:07:05 +02:00
let nuclear_repulsion =
2019-02-20 18:15:15 +01:00
Si.nuclear_repulsion simulation
2018-06-13 17:49:58 +02:00
in
2019-03-01 10:30:02 +01:00
2018-06-13 17:49:58 +02:00
let ao_basis =
2019-02-20 18:15:15 +01:00
Si.ao_basis simulation
2018-05-30 18:07:05 +02:00
in
2018-02-23 18:44:31 +01:00
(* Initial guess *)
let guess =
2018-06-13 17:49:58 +02:00
Guess.make ~nocc ~guess ao_basis
2018-02-23 18:44:31 +01:00
in
(* Orthogonalization matrix *)
let m_X =
2019-02-20 18:24:44 +01:00
Ao.ortho ao_basis
2018-02-23 18:44:31 +01:00
in
2018-02-23 18:44:31 +01:00
(* Overlap matrix *)
let m_S =
2019-02-20 18:24:44 +01:00
Ao.overlap ao_basis
2018-06-13 19:03:42 +02:00
|> Ov.matrix
2018-02-23 18:44:31 +01:00
in
2019-02-20 18:24:44 +01:00
let m_T = Ao.kin_ints ao_basis |> KinInt.matrix
and m_V = Ao.eN_ints ao_basis |> NucInt.matrix
2018-05-30 18:07:05 +02:00
in
2018-02-23 18:44:31 +01:00
2018-05-31 17:48:54 +02:00
(* Level shift in MO basis *)
let m_LSmo =
2018-05-31 16:46:45 +02:00
Array.init (Mat.dim2 m_X) (fun i ->
2019-03-01 10:30:02 +01:00
if i > nocc then level_shift else 0.)
2018-05-31 16:46:45 +02:00
|> Vec.of_array
|> Mat.of_diag
in
2019-03-01 10:30:02 +01:00
(* A single SCF iteration *)
let scf_iteration data =
let nSCF = data.iteration + 1
and m_C = of_some data.coefficients
and m_P_prev = data.density
and fock_prev = data.fock
and diis =
match data.diis with
| Some diis -> diis
| None -> DIIS.make ()
and threshold =
match data.error with
| Some error -> error
| None -> threshold_SCF *. 2.
in
(* Density matrix over nocc occupied MOs *)
let m_P =
gemm ~alpha:2. ~transb:`T ~k:nocc m_C m_C
in
(* Fock matrix in AO basis *)
let fock =
match fock_prev, m_P_prev, threshold > 100. *. threshold_SCF with
| Some fock_prev, Some m_P_prev, true ->
let threshold = 1.e-8 in
Fock.make_rhf ~density:(Mat.sub m_P m_P_prev) ~threshold ao_basis
|> Fock.add fock_prev
| _ -> Fock.make_rhf ~density:m_P ao_basis
in
let m_F, m_Hc, m_J, m_K =
let x = fock in
Fock.(fock x, core x, coulomb x, exchange x)
in
(* Add level shift in AO basis *)
let m_F =
let m_SC =
gemm m_S m_C
2018-02-23 18:44:31 +01:00
in
2019-03-01 10:30:02 +01:00
gemm m_SC (gemm m_LSmo m_SC ~transb:`T)
|> Mat.add m_F
in
2018-05-31 17:48:54 +02:00
2019-03-01 10:30:02 +01:00
(* Fock matrix in orthogonal basis *)
let m_F_ortho =
xt_o_x m_F m_X
in
2018-05-31 17:48:54 +02:00
2019-03-01 10:30:02 +01:00
let error_fock =
let fps =
gemm m_F (gemm m_P m_S)
and spf =
gemm m_S (gemm m_P m_F)
2018-05-31 17:48:54 +02:00
in
2019-03-01 10:30:02 +01:00
xt_o_x (Mat.sub fps spf) m_X
in
2018-05-31 17:48:54 +02:00
2019-03-01 10:30:02 +01:00
let diis =
DIIS.append ~p:(Mat.as_vec m_F_ortho) ~e:(Mat.as_vec error_fock) diis
in
2018-05-31 16:46:45 +02:00
2019-03-01 10:30:02 +01:00
let m_F_diis =
let x =
Bigarray.genarray_of_array1 (DIIS.next diis)
2018-05-31 16:46:45 +02:00
in
2019-03-01 10:30:02 +01:00
Bigarray.reshape_2 x (Mat.dim1 m_F_ortho) (Mat.dim2 m_F_ortho)
in
(* MOs in orthogonal MO basis *)
let m_C', eigenvalues =
diagonalize_symm m_F_diis
in
(* MOs in AO basis *)
let m_C =
gemm m_X m_C'
in
(* Hartree-Fock energy *)
let energy =
nuclear_repulsion +. 0.5 *.
Mat.gemm_trace m_P (Mat.add m_Hc m_F)
in
(* Convergence criterion *)
let error =
error_fock
|> Mat.as_vec
|> amax
|> abs_float
in
{
iteration = nSCF ;
eigenvalues = Some eigenvalues ;
coefficients = Some m_C ;
error = Some error ;
diis = Some diis ;
energy = Some energy ;
density = Some m_P ;
fock = Some fock ;
}
2018-05-31 16:46:45 +02:00
2019-03-01 10:30:02 +01:00
in
2018-05-31 16:46:45 +02:00
2019-03-01 10:30:02 +01:00
let rec make_iterations_list data =
2018-02-23 18:44:31 +01:00
2019-03-01 10:30:02 +01:00
let energy_prev = data.energy in
2018-02-23 18:44:31 +01:00
2019-03-01 10:30:02 +01:00
(** Perform SCF iteration *)
let data = scf_iteration data in
2018-02-23 18:44:31 +01:00
2019-03-01 10:30:02 +01:00
(** Check convergence *)
let converged, error =
match data.error with
| None -> false, 0.
| Some error -> (data.iteration = max_scf || error < threshold_SCF), error
in
2018-02-23 18:44:31 +01:00
2019-03-01 10:30:02 +01:00
(** Print values *)
let nSCF = data.iteration in
2018-05-30 18:07:05 +02:00
2019-03-01 10:30:02 +01:00
let energy = of_some data.energy in
2018-02-23 18:44:31 +01:00
2019-03-01 10:30:02 +01:00
let () =
match energy_prev with
| Some energy_prev ->
Printf.eprintf "%3d %16.10f %16.10f %11.4e\n%!" nSCF energy (energy -. energy_prev) error
| None ->
Printf.eprintf "%3d %16.10f %16s %11.4e\n%!" nSCF energy "" error
in
2018-02-23 18:44:31 +01:00
2019-03-01 10:30:02 +01:00
if converged then
[ data ]
else
{ empty with
iteration = data.iteration;
energy = data.energy ;
eigenvalues = data.eigenvalues ;
error = data.error ;
} :: (make_iterations_list data)
2018-02-23 18:44:31 +01:00
in
2019-03-01 10:30:02 +01:00
2018-02-23 18:44:31 +01:00
(* Guess coefficients *)
let m_H =
match guess with
| Guess.Hcore m_H -> m_H
2018-05-31 16:46:45 +02:00
| Guess.Huckel m_H -> m_H
2018-02-23 18:44:31 +01:00
in
let m_Hmo =
xt_o_x m_H m_X
in
let m_C', _ =
diagonalize_symm m_Hmo
in
let m_C =
gemm m_X m_C'
in
2019-03-01 10:30:02 +01:00
let iterations_list =
make_iterations_list { empty with coefficients = Some m_C }
in
let iterations, data =
List.map (fun data ->
let gap =
let eigenvalues = of_some data.eigenvalues in
if nocc < Vec.dim eigenvalues then
eigenvalues.{nocc+1} -. eigenvalues.{nocc}
else 0.
and energy = of_some data.energy
and error = of_some data.error
in
(energy, error, gap)
) iterations_list
|> Array.of_list,
List.hd (List.rev iterations_list)
in
let energy = of_some data.energy in
let m_P = of_some data.density in
let fock = of_some data.fock in
let m_J = Fock.coulomb fock in
let m_K = Fock.exchange fock in
HartreeFock_type.(
RHF {
simulation;
nocc;
guess ;
eigenvectors = of_some data.coefficients ;
eigenvalues = of_some data.eigenvalues ;
energy ;
nuclear_repulsion;
iterations ;
kin_energy = Mat.gemm_trace m_P m_T;
eN_energy = Mat.gemm_trace m_P m_V;
coulomb_energy = 0.5 *. Mat.gemm_trace m_P m_J;
exchange_energy = 0.5 *. Mat.gemm_trace m_P m_K;
occupation = Mat.copy_diag m_P;
}
)
2018-02-23 18:44:31 +01:00