mirror of
https://gitlab.com/scemama/qmcchem.git
synced 2024-11-09 07:33:39 +01:00
866 lines
16 KiB
OCaml
866 lines
16 KiB
OCaml
open Core.Std
|
|
open Qptypes
|
|
open Qputils
|
|
|
|
|
|
|
|
module Pseudo: sig
|
|
|
|
type t = bool
|
|
val doc : string
|
|
val read : unit -> t
|
|
val to_bool : t -> bool
|
|
val of_bool : bool -> t
|
|
val to_int : t -> int
|
|
val of_int : int -> t
|
|
val to_string : t -> string
|
|
val of_string : string -> t
|
|
|
|
end = struct
|
|
|
|
type t = bool
|
|
|
|
let doc = "Correct wave function to verify electron-nucleus cusp condition"
|
|
|
|
let of_bool x = x
|
|
|
|
let to_bool x = x
|
|
|
|
let read () =
|
|
let _ =
|
|
Lazy.force Qputils.ezfio_filename
|
|
in
|
|
if (not (Ezfio.has_pseudo_do_pseudo ())) then
|
|
Ezfio.set_pseudo_do_pseudo false;
|
|
Ezfio.get_pseudo_do_pseudo ()
|
|
|> of_bool
|
|
|
|
|
|
let to_string t =
|
|
to_bool t
|
|
|> Bool.to_string
|
|
|
|
|
|
let of_string t =
|
|
try
|
|
String.lowercase t
|
|
|> Bool.of_string
|
|
|> of_bool
|
|
with
|
|
| Invalid_argument msg -> failwith msg
|
|
|
|
|
|
let to_int t =
|
|
let t =
|
|
to_bool t
|
|
in
|
|
if t then 1
|
|
else 0
|
|
|
|
|
|
let of_int = function
|
|
| 0 -> false
|
|
| 1 -> true
|
|
| _ -> failwith "Expected 0 or 1"
|
|
|
|
|
|
end
|
|
|
|
module Fitcusp : sig
|
|
|
|
type t = bool
|
|
val doc : string
|
|
val read : unit -> t
|
|
val write : t -> unit
|
|
val to_bool : t -> bool
|
|
val of_bool : bool -> t
|
|
val to_int : t -> int
|
|
val of_int : int -> t
|
|
val to_string : t -> string
|
|
val of_string : string -> t
|
|
|
|
end = struct
|
|
|
|
type t = bool
|
|
|
|
let doc = "Correct wave function to verify electron-nucleus cusp condition"
|
|
|
|
let of_bool x = x
|
|
|
|
let to_bool x = x
|
|
|
|
let read () =
|
|
let _ =
|
|
Lazy.force Qputils.ezfio_filename
|
|
in
|
|
if (not (Ezfio.has_simulation_do_nucl_fitcusp ())) then
|
|
Ezfio.set_simulation_do_nucl_fitcusp Default.simulation_do_nucl_fitcusp;
|
|
Ezfio.get_simulation_do_nucl_fitcusp ()
|
|
|> of_bool
|
|
|
|
|
|
let write t =
|
|
let _ =
|
|
Lazy.force Qputils.ezfio_filename
|
|
in
|
|
let () =
|
|
match (Pseudo.read () |> Pseudo.to_bool, to_bool t) with
|
|
| (true, true) -> failwith "Pseudopotentials and Fitcusp are incompatible"
|
|
| _ -> ()
|
|
in
|
|
to_bool t
|
|
|> Ezfio.set_simulation_do_nucl_fitcusp
|
|
|
|
|
|
let to_string t =
|
|
to_bool t
|
|
|> Bool.to_string
|
|
|
|
|
|
let of_string t =
|
|
try
|
|
String.lowercase t
|
|
|> Bool.of_string
|
|
|> of_bool
|
|
with
|
|
| Invalid_argument msg -> failwith msg
|
|
|
|
|
|
let to_int t =
|
|
let t =
|
|
to_bool t
|
|
in
|
|
if t then 1
|
|
else 0
|
|
|
|
|
|
let of_int = function
|
|
| 0 -> false
|
|
| 1 -> true
|
|
| _ -> failwith "Expected 0 or 1"
|
|
|
|
|
|
end
|
|
|
|
module Block_time : sig
|
|
|
|
type t = int
|
|
val doc : string
|
|
val read : unit -> t
|
|
val write : t -> unit
|
|
val to_int : t -> int
|
|
val of_int : int -> t
|
|
val to_string : t -> string
|
|
val of_string : string -> t
|
|
val to_float : t -> float
|
|
val of_float : float-> t
|
|
|
|
end = struct
|
|
|
|
type t = int
|
|
|
|
let doc = "Length (seconds) of a block"
|
|
|
|
let of_int x =
|
|
if (x < 1) then
|
|
failwith "Block time should be >=1";
|
|
if (x > 36000) then
|
|
failwith "Block time is too large (<= 36000)";
|
|
x
|
|
|
|
|
|
let to_int x = x
|
|
|
|
let read () =
|
|
let _ =
|
|
Lazy.force Qputils.ezfio_filename
|
|
in
|
|
if (not (Ezfio.has_simulation_block_time ())) then
|
|
Ezfio.set_simulation_block_time Default.simulation_block_time;
|
|
Ezfio.get_simulation_block_time ()
|
|
|> of_int
|
|
|
|
|
|
let write t =
|
|
let _ =
|
|
Lazy.force Qputils.ezfio_filename
|
|
in
|
|
to_int t
|
|
|> Ezfio.set_simulation_block_time
|
|
|
|
|
|
let to_string t =
|
|
to_int t
|
|
|> Int.to_string
|
|
|
|
|
|
let of_string t =
|
|
Int.of_string t
|
|
|> of_int
|
|
|
|
|
|
let to_float t =
|
|
to_int t
|
|
|> Float.of_int
|
|
|
|
|
|
let of_float t =
|
|
Int.of_float t
|
|
|> of_int
|
|
|
|
|
|
end
|
|
|
|
module Walk_num : sig
|
|
|
|
type t = int
|
|
val doc : string
|
|
val read : unit -> t
|
|
val write : t -> unit
|
|
val to_int : t -> int
|
|
val of_int : int -> t
|
|
val to_string : t -> string
|
|
val of_string : string -> t
|
|
|
|
end = struct
|
|
|
|
type t = int
|
|
let doc = "Number of walkers per CPU core"
|
|
|
|
let of_int x =
|
|
if (x < 1) then
|
|
failwith "Number of walkers should be >=1";
|
|
if (x > 100_000) then
|
|
failwith "Number of walkers is too large (<= 100_000)";
|
|
x
|
|
|
|
|
|
let to_int x = x
|
|
|
|
let read () =
|
|
let _ =
|
|
Lazy.force Qputils.ezfio_filename
|
|
in
|
|
if (not (Ezfio.has_electrons_elec_walk_num () )) then
|
|
Ezfio.set_electrons_elec_walk_num Default.electrons_elec_walk_num;
|
|
Ezfio.get_electrons_elec_walk_num ()
|
|
|> of_int
|
|
|
|
|
|
let write t =
|
|
let _ =
|
|
Lazy.force Qputils.ezfio_filename
|
|
in
|
|
to_int t
|
|
|> Ezfio.set_electrons_elec_walk_num
|
|
|
|
|
|
let to_string t =
|
|
to_int t
|
|
|> Int.to_string
|
|
|
|
|
|
let of_string t =
|
|
Int.of_string t
|
|
|> of_int
|
|
|
|
|
|
end
|
|
|
|
module Walk_num_tot : sig
|
|
|
|
type t = int
|
|
val doc : string
|
|
val read : unit -> t
|
|
val write : t -> unit
|
|
val to_int : t -> int
|
|
val of_int : int -> t
|
|
val to_string : t -> string
|
|
val of_string : string -> t
|
|
|
|
end = struct
|
|
|
|
type t = int
|
|
let doc = "Total number of stored walkers for restart"
|
|
|
|
let of_int x =
|
|
if (x < 2) then
|
|
failwith "Total number of stored walkers should be > 1";
|
|
if (x > 100_000_000) then
|
|
failwith "Number of walkers to store too large (<= 100.10^6)";
|
|
x
|
|
|
|
|
|
let to_int x = x
|
|
|
|
let read () =
|
|
let _ =
|
|
Lazy.force Qputils.ezfio_filename
|
|
in
|
|
if (not (Ezfio.has_electrons_elec_walk_num_tot () )) then
|
|
Ezfio.set_electrons_elec_walk_num_tot Default.electrons_elec_walk_num_tot;
|
|
Ezfio.get_electrons_elec_walk_num_tot ()
|
|
|> of_int
|
|
|
|
|
|
let write t =
|
|
let _ =
|
|
Lazy.force Qputils.ezfio_filename
|
|
in
|
|
to_int t
|
|
|> Ezfio.set_electrons_elec_walk_num_tot
|
|
|
|
|
|
let to_string t =
|
|
to_int t
|
|
|> Int.to_string
|
|
|
|
|
|
let of_string t =
|
|
Int.of_string t
|
|
|> of_int
|
|
|
|
|
|
end
|
|
|
|
|
|
module Stop_time : sig
|
|
|
|
type t = int
|
|
val read : unit -> t
|
|
val doc : string
|
|
val write : t -> unit
|
|
val to_int : t -> int
|
|
val of_int : int -> t
|
|
val to_float : t -> float
|
|
val of_float : float -> t
|
|
val to_string : t -> string
|
|
val of_string : string -> t
|
|
|
|
end = struct
|
|
|
|
type t = int
|
|
|
|
let doc = "Requested simulation time (seconds)"
|
|
|
|
let of_int x =
|
|
if (x < 1) then
|
|
failwith "Simulation time too short (>=1 s)";
|
|
x
|
|
|
|
|
|
let to_int x = x
|
|
|
|
let read () =
|
|
let _ =
|
|
Lazy.force Qputils.ezfio_filename
|
|
in
|
|
if (not (Ezfio.has_simulation_stop_time ())) then
|
|
Ezfio.set_simulation_stop_time Default.simulation_stop_time;
|
|
Ezfio.get_simulation_stop_time ()
|
|
|> of_int
|
|
|
|
|
|
let write t =
|
|
let _ =
|
|
Lazy.force Qputils.ezfio_filename
|
|
in
|
|
to_int t
|
|
|> Ezfio.set_simulation_stop_time
|
|
|
|
|
|
let to_string t =
|
|
to_int t
|
|
|> Int.to_string
|
|
|
|
|
|
let of_string t =
|
|
Int.of_string t
|
|
|> of_int
|
|
|
|
|
|
let to_float t =
|
|
to_int t
|
|
|> Float.of_int
|
|
|
|
|
|
let of_float t =
|
|
Int.of_float t
|
|
|> of_int
|
|
|
|
end
|
|
|
|
|
|
|
|
module Method : sig
|
|
|
|
type t = VMC | DMC
|
|
val doc : string
|
|
val read : unit -> t
|
|
val write : t -> unit
|
|
val to_string : t -> string
|
|
val of_string : string -> t
|
|
|
|
end = struct
|
|
|
|
type t = VMC | DMC
|
|
|
|
let doc = "QMC Method : [ VMC | DMC ]"
|
|
|
|
let of_string = function
|
|
| "VMC" | "vmc" -> VMC
|
|
| "DMC" | "dmc" -> DMC
|
|
| x -> failwith ("Method should be [ VMC | DMC ], not "^x^".")
|
|
|
|
|
|
let to_string = function
|
|
| VMC -> "VMC"
|
|
| DMC -> "DMC"
|
|
|
|
|
|
let read () =
|
|
let _ =
|
|
Lazy.force Qputils.ezfio_filename
|
|
in
|
|
if (not (Ezfio.has_simulation_method ())) then
|
|
Ezfio.set_simulation_method Default.simulation_method;
|
|
Ezfio.get_simulation_method ()
|
|
|> of_string
|
|
|
|
|
|
let write t =
|
|
let _ =
|
|
Lazy.force Qputils.ezfio_filename
|
|
in
|
|
to_string t
|
|
|> Ezfio.set_simulation_method
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
module Sampling : sig
|
|
|
|
type t = Brownian | Langevin
|
|
val doc : string
|
|
val read : unit -> t
|
|
val write : t -> unit
|
|
val to_string : t -> string
|
|
val of_string : string -> t
|
|
|
|
end = struct
|
|
|
|
type t = Brownian | Langevin
|
|
|
|
let doc = "Sampling algorithm : [ Langevin | Brownian ]"
|
|
|
|
let of_string s =
|
|
match String.capitalize (String.strip s) with
|
|
| "Langevin" -> Langevin
|
|
| "Brownian" -> Brownian
|
|
| x -> failwith ("Sampling should be [ Brownian | Langevin ], not "^x^".")
|
|
|
|
|
|
let to_string = function
|
|
| Langevin -> "Langevin"
|
|
| Brownian -> "Brownian"
|
|
|
|
|
|
let read () =
|
|
let _ =
|
|
Lazy.force Qputils.ezfio_filename
|
|
in
|
|
if (not (Ezfio.has_simulation_sampling ())) then
|
|
Ezfio.set_simulation_sampling Default.simulation_sampling;
|
|
Ezfio.get_simulation_sampling ()
|
|
|> of_string
|
|
|
|
|
|
let write t =
|
|
let _ =
|
|
Lazy.force Qputils.ezfio_filename
|
|
in
|
|
to_string t
|
|
|> Ezfio.set_simulation_sampling
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
module Ref_energy : sig
|
|
|
|
type t = float
|
|
val doc : string
|
|
val read : unit -> t
|
|
val write : t -> unit
|
|
val to_float : t -> float
|
|
val of_float : float -> t
|
|
val to_string : t -> string
|
|
val of_string : string -> t
|
|
|
|
end = struct
|
|
|
|
type t = float
|
|
let doc = "Fixed reference energy to normalize DMC weights"
|
|
|
|
let of_float x =
|
|
if (x > 0.) then
|
|
failwith "Reference energy should not be positive.";
|
|
if (x <= -1_000_000.) then
|
|
failwith "Reference energy is too low.";
|
|
x
|
|
|
|
|
|
let to_float x = x
|
|
|
|
let read () =
|
|
let _ =
|
|
Lazy.force Qputils.ezfio_filename
|
|
in
|
|
if (not (Ezfio.has_simulation_e_ref ())) then
|
|
to_float 0.
|
|
|> Ezfio.set_simulation_e_ref;
|
|
Ezfio.get_simulation_e_ref ()
|
|
|> of_float
|
|
|
|
|
|
let write t =
|
|
let _ =
|
|
Lazy.force Qputils.ezfio_filename
|
|
in
|
|
to_float t
|
|
|> Ezfio.set_simulation_e_ref
|
|
|
|
|
|
let of_string x =
|
|
Float.of_string x
|
|
|> of_float
|
|
|
|
|
|
let to_string x =
|
|
to_float x
|
|
|> Float.to_string
|
|
|
|
|
|
end
|
|
|
|
|
|
module CI_threshold : sig
|
|
|
|
type t = float
|
|
val doc : string
|
|
val read : unit -> t
|
|
val write : t -> unit
|
|
val to_float : t -> float
|
|
val of_float : float -> t
|
|
val to_string : t -> string
|
|
val of_string : string -> t
|
|
|
|
end = struct
|
|
|
|
type t = float
|
|
let doc = "Truncation t of the wave function : Remove determinants with a
|
|
contribution to the norm less than t"
|
|
|
|
let of_float x =
|
|
if (x >= 1.) then
|
|
failwith "Truncation of the wave function should be < 1.";
|
|
if (x < 0.) then
|
|
failwith "Truncation of the wave function should be positive.";
|
|
x
|
|
|
|
|
|
let to_float x = x
|
|
|
|
let read () =
|
|
let _ =
|
|
Lazy.force Qputils.ezfio_filename
|
|
in
|
|
if (not (Ezfio.has_simulation_ci_threshold ())) then
|
|
Ezfio.set_simulation_ci_threshold Default.simulation_ci_threshold ;
|
|
Ezfio.get_simulation_ci_threshold ()
|
|
|> of_float
|
|
|
|
|
|
let write t =
|
|
let _ =
|
|
Lazy.force Qputils.ezfio_filename
|
|
in
|
|
to_float t
|
|
|> Ezfio.set_simulation_ci_threshold
|
|
|
|
|
|
let of_string x =
|
|
Float.of_string x
|
|
|> of_float
|
|
|
|
|
|
let to_string x =
|
|
to_float x
|
|
|> Float.to_string
|
|
|
|
end
|
|
|
|
module Time_step : sig
|
|
|
|
type t = float
|
|
val doc : string
|
|
val read : unit -> t
|
|
val write : t -> unit
|
|
val to_float : t -> float
|
|
val of_float : float -> t
|
|
val to_string : t -> string
|
|
val of_string : string -> t
|
|
|
|
end = struct
|
|
|
|
type t = float
|
|
let doc = "Simulation time step"
|
|
|
|
let of_float x =
|
|
if (x >= 10.) then
|
|
failwith "Time step should be < 10.";
|
|
if (x <= 0.) then
|
|
failwith "Time step should be positive.";
|
|
x
|
|
|
|
|
|
let to_float x = x
|
|
|
|
let read () =
|
|
let _ =
|
|
Lazy.force Qputils.ezfio_filename
|
|
in
|
|
if (not (Ezfio.has_simulation_time_step ())) then
|
|
Ezfio.set_simulation_time_step Default.simulation_time_step;
|
|
Ezfio.get_simulation_time_step ()
|
|
|> of_float
|
|
|
|
|
|
let write t =
|
|
let _ =
|
|
Lazy.force Qputils.ezfio_filename
|
|
in
|
|
to_float t
|
|
|> Ezfio.set_simulation_time_step
|
|
|
|
|
|
let of_string x =
|
|
Float.of_string x
|
|
|> of_float
|
|
|
|
|
|
let to_string x =
|
|
to_float x
|
|
|> Float.to_string
|
|
|
|
end
|
|
|
|
module Jastrow_type : sig
|
|
|
|
type t = None | Core | Simple
|
|
val doc : string
|
|
val read : unit -> t
|
|
val write : t -> unit
|
|
val to_string : t -> string
|
|
val of_string : string -> t
|
|
|
|
end = struct
|
|
|
|
type t = None | Core | Simple
|
|
let doc = "Type of Jastrow factor [ None | Core | Simple ]"
|
|
|
|
let of_string s =
|
|
match String.capitalize (String.strip s) with
|
|
| "Core" -> Core
|
|
| "Simple" -> Simple
|
|
| "None" -> None
|
|
| _ -> failwith "Jastrow type should be [ None | Core | Simple ]"
|
|
|
|
|
|
let to_string = function
|
|
| Core -> "Core"
|
|
| Simple -> "Simple"
|
|
| None -> "None"
|
|
|
|
|
|
let read () =
|
|
let _ =
|
|
Lazy.force Qputils.ezfio_filename
|
|
in
|
|
if (not (Ezfio.has_jastrow_jast_type ())) then
|
|
Ezfio.set_jastrow_jast_type Default.jastrow_jast_type;
|
|
Ezfio.get_jastrow_jast_type ();
|
|
|> of_string
|
|
|
|
|
|
let write t =
|
|
let _ =
|
|
Lazy.force Qputils.ezfio_filename
|
|
in
|
|
let () =
|
|
match (Pseudo.read () |> Pseudo.to_bool, t) with
|
|
| (false, _)
|
|
| (true , None) -> ()
|
|
| _ -> failwith "Jastrow and Pseudopotentials are incompatible"
|
|
in
|
|
|
|
to_string t
|
|
|> Ezfio.set_jastrow_jast_type
|
|
|
|
|
|
end
|
|
|
|
module Properties: sig
|
|
|
|
type t = (Property.t * bool) list
|
|
val doc : string
|
|
val read : unit -> t
|
|
val write : t -> unit
|
|
val to_string : t -> string
|
|
val of_string : string -> t
|
|
|
|
end = struct
|
|
|
|
type t = (Property.t * bool) list
|
|
|
|
let doc =
|
|
"Properties to sample. (X) is true and ( ) is false"
|
|
|
|
|
|
let read () =
|
|
List.map Property.all ~f:(fun x -> (x, Property.calc x))
|
|
|
|
|
|
let write l =
|
|
List.iter l ~f:(fun (x,b) -> Property.set_calc x b)
|
|
|
|
|
|
let to_string l =
|
|
List.map l ~f:(fun (x,b) ->
|
|
let ch =
|
|
if b then "X" else " "
|
|
in
|
|
Printf.sprintf "(%s) %s" ch (Property.to_string x))
|
|
|> String.concat ~sep:"\n"
|
|
|
|
|
|
let of_string s =
|
|
String.split s ~on:'\n'
|
|
|> List.map ~f:(fun x ->
|
|
let (calc,prop) =
|
|
String.strip x
|
|
|> String.rsplit2_exn ~on:' '
|
|
in
|
|
let prop =
|
|
String.strip prop
|
|
|> Property.of_string
|
|
and calc =
|
|
match calc with
|
|
| "(X)" -> true
|
|
| "( )" -> false
|
|
| _ -> failwith " (X) or ( ) expected"
|
|
in
|
|
(prop, calc)
|
|
)
|
|
|
|
end
|
|
|
|
(** Check if everything is correct in the input file. *)
|
|
let validate () =
|
|
|
|
let _ =
|
|
Lazy.force Qputils.ezfio_filename
|
|
in
|
|
|
|
(* Check if walkers are present *)
|
|
if (not (Ezfio.has_electrons_elec_coord_pool ())) then
|
|
Printf.printf "Warning: No initial walkers\n";
|
|
|
|
let meth =
|
|
Method.read ()
|
|
and sampling =
|
|
Sampling.read ()
|
|
and ts =
|
|
Time_step.read ()
|
|
and jast_type =
|
|
Jastrow_type.read ()
|
|
and do_fitcusp =
|
|
Fitcusp.read ()
|
|
and do_pseudo =
|
|
Pseudo.read ()
|
|
in
|
|
|
|
(* Check sampling and time steps *)
|
|
let () =
|
|
match (sampling, meth, Pseudo.to_bool do_pseudo) with
|
|
| (Sampling.Brownian, Method.DMC, true) ->
|
|
if ( (Time_step.to_float ts) >= 0.5 ) then
|
|
warn "Time step seems large for DMC.";
|
|
| (Sampling.Brownian, Method.DMC, false) ->
|
|
if ( (Time_step.to_float ts) >= 0.01 ) then
|
|
warn "Time step seems large for DMC.";
|
|
| (Sampling.Brownian, Method.VMC, _) ->
|
|
if ( (Time_step.to_float ts) >= 10. ) then
|
|
warn "Time step seems large for VMC.";
|
|
| (Sampling.Langevin, Method.VMC, _) ->
|
|
if ( (Time_step.to_float ts) <= 0.01 ) then
|
|
warn "Time step seems small for Langevin sampling."
|
|
| (Sampling.Langevin, Method.DMC, _) ->
|
|
failwith "Lanvegin sampling is incompatible with DMC"
|
|
in
|
|
|
|
|
|
(* Check E_ref is not zero *)
|
|
let () =
|
|
match (meth, Ref_energy.(read () |> to_float) ) with
|
|
| (Method.DMC,0.) -> failwith "E_ref should not be zero in DMC"
|
|
| _ -> ()
|
|
in
|
|
|
|
(* Set block and total time*)
|
|
let () =
|
|
if ( (Block_time.read ()) > Stop_time.read ()) then
|
|
failwith "Block time is longer than total time"
|
|
in
|
|
|
|
(* Check if E_loc if computed *)
|
|
let () =
|
|
match (meth, Property.(calc E_loc)) with
|
|
| (Method.DMC, false) -> failwith "E_loc should be sampled in DMC"
|
|
| (Method.VMC, false) -> warn "Sampling of E_loc is not activated in input"
|
|
| _ -> ()
|
|
in
|
|
|
|
(* Pseudo and Jastrow are incompatible *)
|
|
let () =
|
|
match (Pseudo.to_bool do_pseudo, jast_type) with
|
|
| (true, Jastrow_type.Core )
|
|
| (true, Jastrow_type.Simple) -> failwith "Jastrow and Pseudopotentials are incompatible"
|
|
| _ -> ()
|
|
in
|
|
|
|
(* Fitcusp is not recommended with pseudo *)
|
|
let () =
|
|
match (Pseudo.to_bool do_pseudo, Fitcusp.to_bool do_fitcusp) with
|
|
| (true, true) -> warn "Fitcusp is incompatible with Pseudopotentials"
|
|
| _ -> ()
|
|
in
|
|
|
|
(* Other Checks *)
|
|
let () =
|
|
let _ =
|
|
Walk_num.read ()
|
|
and _ =
|
|
Walk_num_tot.read ()
|
|
and _ =
|
|
CI_threshold.read ()
|
|
in ()
|
|
in
|
|
()
|
|
|
|
|
|
|