open Util open Constants exception Null_contribution type t = { coefs_and_shell_pairs : (float * PrimitiveShellPair.t) list; shell_a : ContractedShell.t; shell_b : ContractedShell.t; } module Am = AngularMomentum module Co = Coordinate module Cs = ContractedShell module Ps = PrimitiveShell module Psp = PrimitiveShellPair (** Creates an contracted shell pair : an array of pairs of primitive shells. A contracted shell with N functions combined with a contracted shell with M functions generates a NxM array of shell pairs. *) let make ?(cutoff=Constants.epsilon) s_a s_b = let make = Psp.create_make_of (Cs.primitives s_a).(0) (Cs.primitives s_b).(0) in let coefs_and_shell_pairs = Array.mapi (fun i p_a -> let c_a = (Cs.coefficients s_a).(i) in let make = make p_a in Array.mapi (fun j p_b -> let c_b = (Cs.coefficients s_b).(j) in let coef = c_a *. c_b in assert (coef <> 0.); let cutoff = cutoff /. abs_float coef in coef, make p_b cutoff) (Cs.primitives s_b)) (Cs.primitives s_a) |> Array.to_list |> Array.concat |> Array.to_list |> List.filter (function (_, Some _) -> true | _ -> false) |> List.map (function (c, Some x) -> (c *. Psp.normalization x, x) | _ -> assert false) in match coefs_and_shell_pairs with | [] -> None | coefs_and_shell_pairs -> Some { shell_a = s_a ; shell_b = s_b ; coefs_and_shell_pairs } let shell_a x = x.shell_a let shell_b x = x.shell_b let coefs_and_shell_pairs x = x.coefs_and_shell_pairs let shell_pairs x = List.map snd x.coefs_and_shell_pairs |> Array.of_list let coefficients x = List.map fst x.coefs_and_shell_pairs |> Array.of_list let exponents_inv x = List.map (fun (_,sp) -> Psp.exponent_inv sp) x.coefs_and_shell_pairs |> Array.of_list let a_minus_b x = match x.coefs_and_shell_pairs with | [] -> assert false | (_,sp)::_ -> Psp.a_minus_b sp let a_minus_b_sq x = match x.coefs_and_shell_pairs with | [] -> assert false | (_,sp)::_ -> Psp.a_minus_b_sq sp let ang_mom x = match x.coefs_and_shell_pairs with | [] -> assert false | (_,sp)::_ -> Psp.ang_mom sp let norm_scales x = match x.coefs_and_shell_pairs with | [] -> assert false | (_,sp)::_ -> Psp.norm_scales sp let monocentric x = match x.coefs_and_shell_pairs with | [] -> assert false | (_,sp)::_ -> Psp.monocentric sp (** Returns an integer characteristic of a contracted shell pair *) let hash a = Array.map Hashtbl.hash a (** Comparison function, used for sorting *) let cmp a b = if a = b then 0 else if (Array.length a < Array.length b) then -1 else if (Array.length a > Array.length b) then 1 else let out = ref 0 in begin try for k=0 to (Array.length a - 1) do if a.(k) < b.(k) then (out := (-1); raise Not_found) else if a.(k) > b.(k) then (out := 1; raise Not_found); done with Not_found -> (); end; !out (** The array of all shell pairs with their correspondance in the list of contracted shells. *) let of_contracted_shell_array ?(cutoff=Constants.epsilon) basis = let rec loop accu = function | [] -> accu | (s_a :: rest) as l -> let new_accu = (List.map (fun s_b -> make ~cutoff s_a s_b) l) :: accu in (loop [@tailcall]) new_accu rest in loop [] (List.rev (Array.to_list basis)) |> List.concat |> list_some let equivalent x y = (Array.length x = Array.length y) && let rec eqv = function | 0 -> true | k -> if Psp.equivalent x.(k) y.(k) then (eqv [@tailcall]) (k-1) else false in eqv (Array.length x - 1) (** A list of unique shell pairs *) let unique sp = let sp = Array.to_list sp |> Array.concat |> Array.to_list in let rec aux accu = function | [] -> accu | x::rest -> let newaccu = try ignore @@ List.find (fun y -> equivalent x y) accu; accu with Not_found -> (x::accu) in (aux [@tailcall]) newaccu rest in aux [] sp let zkey_array x = Am.zkey_array (Am.Doublet Cs.(ang_mom x.shell_a, ang_mom x.shell_b) )