#+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 

* Electrons
  :PROPERTIES:
  :header-args: :noweb yes :comments both
  :END:

  Data structure which contains the number of \alpha and \beta electrons.

** Type

   #+NAME: types
   #+begin_src ocaml :tangle (eval mli)
type t 
   #+end_src

   #+begin_src ocaml :tangle (eval ml) :exports none
type t = {
  n_alfa : int ;
  n_beta : int ;
}
   #+end_src

   #+begin_src ocaml :tangle (eval test-ml) :exports none
open Common
open Particles
open Alcotest

let test_all () =
  let nuclei =
"3
Water
O       0.                     0.   0.
H      -0.756950272703377558   0.  -0.585882234512562827
H       0.756950272703377558   0.  -0.585882234512562827
"
  |> Nuclei.of_xyz_string 
  in
  let e = Electrons.of_atoms nuclei in
   #+end_src

** Creation

   #+begin_src ocaml :tangle (eval mli)
open Common

val make : int -> int -> t

val of_atoms : ?multiplicity:int -> ?charge:int -> Nuclei.t -> t
(* @param multiplicity default is 1
   @param charge       default is 0
   @raise Invalid_argument if the spin multiplicity is not compatible with
    the molecule and the total charge.
*)
   #+end_src

   | ~make~     | ~make n_alfa n_beta~                                                                                                                  |
   | ~of_atoms~ | Creates the data relative to electrons for a molecular system described by ~Nuclei.t~ for a given total charge and spin multiplicity. |

   #+begin_src ocaml :tangle (eval ml) :exports none
open Common

let make n_alfa n_beta = 
  { n_alfa ; n_beta }


let of_atoms ?multiplicity:(multiplicity=1) ?charge:(charge=0) nuclei =
  let positive_charges =
    Array.fold_left (fun accu (e, _) -> accu + Charge.to_int (Element.to_charge e) )
      0 nuclei
  in
  let negative_charges = charge - positive_charges in
  let n_elec = - negative_charges in
  let n_beta = ((n_elec - multiplicity)+1)/2 in
  let n_alfa = n_elec - n_beta in
  let result = { n_alfa ; n_beta } in
  if multiplicity <> (n_alfa - n_beta)+1 then
    invalid_arg (__FILE__^": make");
  result

   #+end_src

   #+begin_src ocaml :tangle (eval test-ml) :exports none
check int "of_atoms alfa" 5 (Electrons.n_alfa e);
check int "of_atoms beta" 5 (Electrons.n_beta e);
   #+end_src

** Access

   #+begin_src ocaml :tangle (eval mli)
val charge       : t -> Charge.t
val n_elec       : t -> int
val n_alfa       : t -> int
val n_beta       : t -> int
val multiplicity : t -> int 
   #+end_src

   | ~charge~       | Sum of the charges of the electrons |
   | ~n_elec~       | Number of electrons                 |
   | ~n_alfa~       | Number of alpha electrons           |
   | ~n_beta~       | Number of beta  electrons           |
   | ~multiplicity~ | Spin multiplicity: $2S+1$           |

   #+begin_src ocaml :tangle (eval ml) :exports none
let charge e =
  - (e.n_alfa + e.n_beta)
  |> Charge.of_int

let n_alfa t = t.n_alfa

let n_beta t = t.n_beta

let n_elec t = t.n_alfa + t.n_beta

let multiplicity t = t.n_alfa - t.n_beta + 1
   #+end_src

   #+begin_src ocaml :tangle (eval test-ml) :exports none
check int "charge " (-10) (Charge.to_int @@ Electrons.charge e);
check int "n_elec" 10 (Electrons.n_elec e);
check int "multiplicity" 1 (Electrons.multiplicity e);
check int "of_atoms alfa m3" 6 (Electrons.(of_atoms ~multiplicity:3 nuclei |> n_alfa));
check int "of_atoms beta m3" 4 (Electrons.(of_atoms ~multiplicity:3 nuclei |> n_beta));
check int "of_atoms n_elec m3" 10 (Electrons.(of_atoms ~multiplicity:3 nuclei |> n_elec));
check int "of_atoms alfa m2 c1" 5 (Electrons.(of_atoms ~multiplicity:2 ~charge:1 nuclei |> n_alfa));
check int "of_atoms beta m2 c1" 4 (Electrons.(of_atoms ~multiplicity:2 ~charge:1 nuclei |> n_beta));
check int "of_atoms beta m2 c1" 9 (Electrons.(of_atoms ~multiplicity:2 ~charge:1 nuclei |> n_elec));
check int "of_atoms mult m2 c1" 2 (Electrons.(of_atoms ~multiplicity:2 ~charge:1 nuclei |> multiplicity));
check bool "make" true (Electrons.make 6 4 = Electrons.(of_atoms ~multiplicity:3 nuclei));
   #+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 pp ppf t =
  Format.fprintf ppf "@[n_alfa=%d, n_beta=%d@]" t.n_alfa t.n_beta
   #+end_src

** Tests

   #+begin_src ocaml :tangle (eval test-ml) :exports none
  ()

let tests = [
  "all", `Quick, test_all
]
   #+end_src
