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

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

  Coordinates in 3D space.
  
  All operations on points are done in atomic units. Therefore, 
  all coordinates are given in bohr and manipulated with this
  module.

** Type

   <<<~Coordinate.t~>>>
   #+NAME: types
   #+begin_src ocaml :tangle (eval mli)
type bohr 
type angstrom 

type xyz = {
  x : float ;
  y : float ;
  z : float ;
}

type 'a point = xyz

type t = bohr point

type axis = X | Y | Z
   #+end_src

   #+begin_src ocaml :tangle (eval ml) :exports none
type bohr 
type angstrom 

type xyz = {
  x : float ;
  y : float ;
  z : float ;
}

type 'a point = xyz

type t = bohr point

type axis = X | Y | Z
   #+end_src

** Creation 

   #+begin_src ocaml :tangle (eval mli)
val make          : 'a point -> t
val make_angstrom : 'a point -> angstrom point
val zero          : bohr point
   #+end_src

   | ~make~          | Creates a point in atomic units |
   | ~make_angstrom~ | Creates a point in angstrom     |
   | ~zero~          | $(0., 0., 0.)$                  |

   #+begin_src ocaml :tangle (eval ml) :exports none
external make          : 'a point -> t              = "%identity"
external make_angstrom : 'a point -> angstrom point = "%identity"

let zero =
  make { x = 0. ; y = 0. ; z = 0. }
   #+end_src

** Conversion

   #+begin_src ocaml :tangle (eval mli)
val bohr_to_angstrom : bohr point -> angstrom point
val angstrom_to_bohr : angstrom point -> bohr point
   #+end_src

   | ~bohr_to_angstrom~ | Converts a point in bohr to angstrom |
   | ~angstrom_to_bohr~ | Converts a point in angstrom to bohr |

   #+begin_src ocaml :tangle (eval ml) :exports none
let b_to_a b = Constants.a0 *. b 
let bohr_to_angstrom { x ; y ; z } =
  make { x = b_to_a x ;
         y = b_to_a y ;
         z = b_to_a z ; }


let a_to_b a = Constants.a0_inv *. a 
let angstrom_to_bohr { x ; y ; z } =  
  make { x = a_to_b x ;
         y = a_to_b y ;
         z = a_to_b z ; }
   #+end_src

** Vector operations

   #+begin_src ocaml :tangle (eval mli)
val neg    : t -> t
val get    : axis -> bohr point -> float
val dot    : t -> t -> float
val norm   : t -> float
val ( |. ) : float -> t -> t
val ( |+ ) : t -> t -> t
val ( |- ) : t -> t -> t
   #+end_src

   | ~neg~     | Negative of a point                                  |
   | ~get~     | Extracts the projection of the coordinate on an axis |
   | ~dot~     | Dot product                                          |
   | ~norm~    | $\ell{^2}$ norm of the vector                        |
   | $\vert .$ | Scales the vector by a constant                      |
   | $\vert +$ | Adds two vectors                                     |
   | $\vert -$ | Subtracts two vectors                                |

   Example:
   #+begin_example
Coordinate.neg { x=1. ; y=2. ; z=3. } ;;
- : Coordinate.t =  -1.0000  -2.0000  -3.0000

Coordinate.(get Y { x=1. ; y=2. ; z=3. }) ;;
- : float = 2.

Coordinate.(
  2. |. { x=1. ; y=2. ; z=3. }
) ;;
- : Coordinate.t =   2.0000   4.0000   6.0000

Coordinate.(
  { x=1. ; y=2. ; z=3. } |+ { x=2. ; y=3. ; z=1. } 
);;
- : Coordinate.t =   3.0000   5.0000   4.0000

Coordinate.(
  { x=1. ; y=2. ; z=3. } |- { x=2. ; y=3. ; z=1. } 
);;
- : Coordinate.t =  -1.0000  -1.0000   2.0000
   #+end_example


   #+begin_src ocaml :tangle (eval ml) :exports none
let get axis { x ; y ; z } = 
  match axis with
  | X -> x 
  | Y -> y 
  | Z -> z  


let ( |. )  s { x ; y ; z } =
  make { x = s *. x ;
         y = s *. y ;
         z = s *. z ; }


let ( |+ )
    { x = x1 ; y = y1 ; z = z1 }
    { x = x2 ; y = y2 ; z = z2 } =
  make { x = x1 +. x2 ;
         y = y1 +. y2 ;
         z = z1 +. z2 ; }


let ( |- )
    { x = x1 ; y = y1 ; z = z1 }
    { x = x2 ; y = y2 ; z = z2 } =
  make { x = x1 -. x2 ;
         y = y1 -. y2 ;
         z = z1 -. z2 ; }


let neg a = -1. |. a


let dot
    { x = x1 ; y = y1 ; z = z1 }
    { x = x2 ; y = y2 ; z = z2 } =
  x1 *. x2 +.
  y1 *. y2 +.
  z1 *. z2


let norm u =
  sqrt ( dot u u )
   #+end_src

** Printers

   #+begin_src ocaml :tangle (eval mli)
val pp : Format.formatter -> t -> unit
val pp_bohr: Format.formatter -> t -> unit
val pp_angstrom : Format.formatter -> t -> unit
   #+end_src

   Coordinates can be printed in bohr or angstrom.

   #+begin_src ocaml :tangle (eval ml) :exports none
open Format
let pp ppf c = 
  fprintf ppf "@[@[%8.4f@] @[%8.4f@] @[%8.4f@]@]" c.x c.y c.z

let pp_bohr ppf c = 
  fprintf ppf "@[(@[%10f@], @[%10f@], @[%10f@] Bohr)@]" c.x c.y c.z

let pp_angstrom ppf c = 
  let c = bohr_to_angstrom c in
  fprintf ppf "@[(@[%10f@], @[%10f@], @[%10f@] Angs)@]" c.x c.y c.z
   #+end_src

