Index: trunk/LMDZ.TITAN/README
===================================================================
--- trunk/LMDZ.TITAN/README	(revision 1896)
+++ trunk/LMDZ.TITAN/README	(revision 1897)
@@ -1401,2 +1401,9 @@
 Added a bash script ( utilities/prepare_startkim.bash ) to create startfi files
 with upper_chemistry_layers dimension and fill fields from old comp_xx files
+
+== 24/01/18 == JVO
+Major updates in YAMMS and it's coupling from J.Burgalat, including
++ Added the microphysics optical routines and coupling of YAMMS haze with the radiative transfer
++ ... yet you shouldn't activate it yet ( flag uncoupl_optic_haze has to be true ) !
++ Some supplementary safety checks to keep microphysics spitting negative tendencies
++ Other upkeep changes deeper in the library
Index: trunk/LMDZ.TITAN/libf/muphytitan/argparse.F90
===================================================================
--- trunk/LMDZ.TITAN/libf/muphytitan/argparse.F90	(revision 1896)
+++ trunk/LMDZ.TITAN/libf/muphytitan/argparse.F90	(revision 1897)
@@ -1,5 +1,5 @@
-! Copyright Jérémie Burgalat (2010-2015)
+! Copyright Jérémie Burgalat (2010-2015,2017)
 ! 
-! burgalat.jeremie@gmail.com
+! jeremie.burgalat@univ-reims.fr
 ! 
 ! This software is a computer program whose purpose is to provide configuration 
@@ -33,7 +33,7 @@
 
 !! file: argparse.F90
-!! summary: Command-line parser source file
-!! author: burgalat
-!! date: 2013-2015
+!! summary: Command-line parser source file.
+!! author: J. Burgalat
+!! date: 2013-2015,2017
 
 #include "defined.h"
@@ -47,15 +47,10 @@
   !! 
   !! If you only wish to have an overview of argparse usage, you'd better go
-  !! [p_argparse](here).
+  !! [here](|url|/page/swift/p01_argparse.html).
+
   USE, INTRINSIC :: ISO_FORTRAN_ENV, ONLY : stdout=>OUTPUT_UNIT, stderr=>ERROR_UNIT
   USE ERRORS
   USE FSYSTEM, ONLY : fs_termsize
-  USE STRINGS, getpar => format_paragraph, &
-               splitstr  => format_string, & 
-               ap_string  => st_string,    & !! String value type ID
-               ap_complex  => st_complex,  & !! Complex value type ID
-               ap_logical => st_logical,   & !! Logical value type ID
-               ap_integer => st_integer,   & !! Integer value type ID
-               ap_real    => st_real         !! Floating point value type ID 
+  USE STRING_OP, getpar => format_paragraph, splitstr  => format_string  
   IMPLICIT NONE
 
@@ -66,8 +61,5 @@
   PUBLIC :: noerror,error, error_to_string,aborting
   ! from strings
-#if ! HAVE_FTNDTSTR
   PUBLIC :: st_slen, st_llen
-#endif
-  PUBLIC :: ap_string, ap_complex, ap_logical, ap_integer, ap_real
   PUBLIC :: stderr, stdout
   ! argparse module
@@ -92,4 +84,17 @@
   ! PARAMETERS (INTRISIC TYPES)
   ! ===========================
+
+  INTEGER, PARAMETER, PUBLIC :: ap_string  = st_string 
+    !! String value type identifier.
+  INTEGER, PARAMETER, PUBLIC :: ap_complex = st_complex
+    !! Complex value type identifier.
+  INTEGER, PARAMETER, PUBLIC :: ap_logical = st_logical
+    !! Logical value type identifier.
+  INTEGER, PARAMETER, PUBLIC :: ap_integer = st_integer
+    !! Integer value type identifier.
+  INTEGER, PARAMETER, PUBLIC :: ap_real = st_real
+    !! Real value type identifier.
+
+  !> List of all available actions
 
   INTEGER, PARAMETER, PUBLIC :: ap_store  = 1 
@@ -120,4 +125,44 @@
     
   !> Add an option to the parser
+  !!
+  !! ```
+  !! FUNCTION argparser_add_option(this,dest,sflag,lflag,type,action,default,nrec,help,meta) RESULT(err)
+  !!          argparser_add_option(this,dest,flag,type,action,default,nrec,help,meta) RESULT(err)
+  !! ```
+  !!
+  !! The function defines a new argument based on input parameters, checks it and finally sets it 
+  !! in the parser. 
+  !! 
+  !! In its first version both short (`sflag`) and long (`lflag`) options flags are mandatory. In its second
+  !! form, a single flag (`flag`) is expected: the method will automatically deduce if it belongs to short or
+  !! a long option flag based on the number of hyphens given.
+  !! 
+  !! `type` value should be one of the following module constants (which are aliases from [[string_op(module)]]):
+  !!
+  !! - `ap_string` ([[string_op(module):st_string(variable)]])
+  !! - `ap_complex` ([[string_op(module):st_complex(variable)]])
+  !! - `ap_logical` ([[string_op(module):st_logical(variable)]])
+  !! - `ap_integer` ([[string_op(module):st_integer(variable)]])
+  !! - `ap_real` ([[string_op(module):st_real(variable)]])
+  !! 
+  !! `action` value should be one of the following module constants:
+  !!
+  !! - [[argparse(module):ap_store(variable)]]
+  !! - [[argparse(module):ap_append(variable)]]
+  !! - [[argparse(module):ap_count(variable)]]
+  !! - [[argparse(module):ap_help(variable)]]
+  !!
+  !! `nrec` string can take the following forms:
+  !!
+  !! tag   | description
+  !! :---: | : -------------
+  !!  "?"  | zero or one argument's value
+  !!  "*"  | any number of arguments
+  !!  "+"  | one or more argument's value(s)
+  !!  "X"  | Exactly X values. Where X is the string representation of an integer (0 is accepted).
+  !!
+  !! On success, `noerror` is returned. Otherwise -9 error code is returned. Errors are only 
+  !! produced by misuse of the function arguments. In such case, the program should be
+  !! stopped: note that such error should not occur in _released_ programs.
   INTERFACE argparser_add_option
     MODULE PROCEDURE ap_add_option_1, ap_add_option_2
@@ -131,5 +176,10 @@
   !> Get optional argument value(s)
   !!
-  !! This is the generic method that can be used to retrieve any kind of argument value(s) from the parser.
+  !! ```
+  !! FUNCTION argparser_get_value(this,name,output) RESULT(err)
+  !! ```
+  !! 
+  !! This is the generic method that can be used to retrieve any kind of argument value(s) from the parser for a given
+  !! argument name (as defined by the `dest` argument of [[argparse(module):argparser_add_option(interface)]].
   !! All the methods have the same dummy arguments only `output` dummy argument differs in type and shape.
   !! 
@@ -137,5 +187,5 @@
   !! For string vector, `output` is expected to be an allocatable vector of **assumed length**
   !! strings (thus string length is left to user responsability).
-  !! A good compromise for strings length is to use the [[strings(module):st_slen(variable)]] 
+  !! A good compromise for strings length is to use the [[string_op(module):st_slen(variable)]] 
   !! parameter.
   INTERFACE argparser_get_value
@@ -287,6 +337,4 @@
     PROCEDURE, PRIVATE :: ap_get_sv_ve
     PROCEDURE, PRIVATE :: ap_check_state
-    !PROCEDURE, PUBLIC  :: reset_values       => argparser_reset_values
-    !  !! Resets values stored in the parser
     PROCEDURE, PUBLIC  :: throw_error        => argparser_throw_error
       !! Throw an error and exit the program
@@ -415,9 +463,9 @@
 
   FUNCTION argparser_add_positionals(this,nargs,meta,help) RESULT(err)
-    !! Add positional arguments definition to the parser
+    !! Add positional arguments definition to the parser.
+    !!
     !! The method initializes the entry for positional arguments in the parser.
     !! Positional arguments are always seen by the parser as strings and the 
     !! default associated action is 'store'.
-
     OBJECT(argparser), INTENT(inout)                     :: this
       !! An argparser object reference
@@ -533,5 +581,5 @@
     IF (zauto) THEN
       IF (rhelp) CALL argparser_help(this)
-      IF (err /= 0.AND. err /= 255) CALL argparser_throw_error(this,err,2)
+      IF (err /= 0) CALL argparser_throw_error(this,err,2)
     ENDIF
     RETURN 
@@ -751,10 +799,10 @@
     !! in the parser. Both **short and long options flags** are mandatory input arguments of the function.
     !! 
-    !!  `type` value should be one of the following module constants (which are aliases from [[strings(module)]]):
-    !!  - ap_string ([[strings(module):st_string(variable)]])
-    !!  - ap_complex ([[strings(module):st_complex(variable)]])
-    !!  - ap_logical ([[strings(module):st_logical(variable)]])
-    !!  - ap_integer ([[strings(module):st_integer(variable)]])
-    !!  - ap_real ([[strings(module):st_real(variable)]])
+    !!  `type` value should be one of the following module constants (which are aliases from [[string_op(module)]]):
+    !!  - ap_string ([[string_op(module):st_string(variable)]])
+    !!  - ap_complex ([[string_op(module):st_complex(variable)]])
+    !!  - ap_logical ([[string_op(module):st_logical(variable)]])
+    !!  - ap_integer ([[string_op(module):st_integer(variable)]])
+    !!  - ap_real ([[string_op(module):st_real(variable)]])
     !! 
     !!  `action` value should be one of the following module constants:
@@ -772,5 +820,5 @@
     !!   X  | Exactly X values. Where X is the string representation of an integer (0 is accepted).
     !!
-    !! See also [[argparse(module):ap_add_option_2(function)]] documentation.
+    !! See also ap_add_option_2 documentation.
     OBJECT(argparser), INTENT(inout)                     :: this
       !! An argparser object reference
@@ -828,10 +876,10 @@
     !! Add an argument to the parser (interface #2)
     !!
-    !! The function is a wrapper to [[argparse(module):ap_add_option_1(function)]]. In this version, 
+    !! The function is a wrapper to ap_add_option_1. In this version, 
     !! only one option flag is required. The method only checks for the (trimmed) length of **flag** in
     !! order to choose wether it is a **short** or **long** option flag. Then the function simply calls 
-    !! [[argparse(module):ap_add_option_1(function)]] to set the argument.
+    !! ap_add_option_1 to set the argument.
     !! 
-    !! Other dummy arguments have the same meaning as in [[argparse(module):ap_add_option_1(function)]].
+    !! Other dummy arguments have the same meaning as in ap_add_option_1.
     OBJECT(argparser), INTENT(inout)                     :: this
       !! An argparser object reference
@@ -886,5 +934,5 @@
       !! An argc object to check
     TYPE(error) :: err
-      !! Error object with -15 id if object is already set, no error otherwise.
+      !! Error object with -8 id if object is already set, no error otherwise.
     INTEGER :: i
     err = noerror
@@ -914,5 +962,5 @@
       !! An argparser object reference
     TYPE(words), INTENT(inout)               :: cmd
-      !! A [[strings(module):words(type)]] object with the command-line to parse
+      !! A [[string_op(module):words(type)]] object with the command-line to parse
     LOGICAL, INTENT(out)                     :: help_req
       !! An output logical flag with `.true.` if help option has been found, `.false.` otherwise
@@ -987,5 +1035,5 @@
       !! An argparser object reference
     TYPE(words), INTENT(inout)       :: cmd
-      !! A [[strings(module):words(type)]] object with the command-line to parse
+      !! A [[string_op(module):words(type)]] object with the command-line to parse
     TYPE(error) :: err
       !! Error object with the first error encountered in the process
@@ -1068,5 +1116,5 @@
     ! Gets values as a function of the expected number of records
     IF (arg%nrec == 0) THEN
-      ! parse_args (main parsing method) reset all values: for 0 nrec
+      ! argparser_parse (main parsing method) reset all values: for 0 nrec
       ! we should have at least one value saved which is the default.
       ! if it is not the case we set default as values of the argument
@@ -1136,5 +1184,5 @@
     !! @warning
     !! For compilers that does not support allocatable strings in derived types,
-    !! computation are highly dependent of [[strings(module):st_slen(variable):
+    !! computation are highly dependent of [[string_op(module):st_slen(variable):
     !! tokens length are limited by this parameter. 
     IMPLICIT NONE
@@ -1144,5 +1192,5 @@
       !! A string to process
     TYPE(words), INTENT(out)      :: new_cmd
-      !! An output [[strings(module):words(type)]] object with the processed command line
+      !! An output [[string_op(module):words(type)]] object with the processed command line
     LOGICAL, INTENT(out)          :: rhelp   
       !! An output boolean flag with `.true.` if help is requested, `.false.` otherwise
@@ -1155,9 +1203,9 @@
     err = noerror ; rhelp = .false.
     IF (LEN_TRIM(string) == 0) THEN
-      err = error('internal error (empty string)',-2) 
+      err = error('internal error (empty string)',-255) 
       RETURN
     ENDIF
     ! split input command line in words !
-    splitted = new_words(string," ",.true.) ! tokens may be truncated :( 
+    call words_extend(splitted,string," ",.true.)
     ! reset iterator
     CALL words_reset(splitted)
@@ -1330,5 +1378,6 @@
 
   SUBROUTINE ap_format_usage(this,optmw)
-    !! Format command line usage
+    !! Format command line usage.
+    !!
     !! The subroutine creates and formats the command line usage of the 
     !! given argparser object. If [[argparser(type):usg(variable)]] is already set (i.e. not empty) 
@@ -1693,6 +1742,8 @@
     !! The function calls all the tests to perform on argc members. Some of these tests can 
     !! alter argc's member values to fit argparser's requirements. 
-    !! @note
-    !! Normally, program should be stopped if returned error is not 0 !
+    !!
+    !! On success, `noerror` is returned. Otherwise -9 error code is returned. Errors are only 
+    !! produced by misuse of the function arguments. In such case, the program should be
+    !! stopped: note that such ezrror should not occur in _released_ programs.
     TYPE(argc), INTENT(inout)                            :: this
       !! An argc object
@@ -1740,4 +1791,7 @@
     !! The method checks if input argument's options are valid and update the argc object 
     !! consequently. 
+    !!
+    !! On success, `noerror` is returned. Otherwise -9 error code is returned. Errors are only 
+    !! produced by misuse of the function arguments.
     TYPE(argc), INTENT(inout)  :: this
       !! An argc object to update
@@ -1756,6 +1810,6 @@
     ret = noerror
     eprf = 'argparse: '//"Invalid argument `"//TRIM(this%name)//"'"
-    uty = error(eprf//" (type)",-2) 
-    ina = error(eprf//" (inconsistent nargs)",-2)
+    uty = error(eprf//" (type)",-9) 
+    ina = error(eprf//" (inconsistent nargs)",-9)
     zna = TRIM(na)
     ! Checks action
@@ -1763,5 +1817,5 @@
       this%paction = ac 
     ELSE
-      ret = error(eprf//" (action)",-2) ; RETURN 
+      ret = error(eprf//" (action)",-9) ; RETURN 
     ENDIF
     ! Checks and sets type and default as a function of the action
@@ -1885,5 +1939,5 @@
             terr = error(eprf//" (inconsistent default value: expected '"// &
                            TRIM(st_type_names(this%ptype))//"', found '"// &
-                           TRIM(st_type_names(t))//"')",-2)
+                           TRIM(st_type_names(t))//"')",-9)
             RETURN
           ENDIF
@@ -1977,5 +2031,5 @@
       SELECT CASE(this%nrec)
         CASE(-3,-2,-1)
-          zmeta = str_to_upper(meta(1))
+          zmeta = to_upper(meta(1))
           blk = INDEX(TRIM(zmeta),CHAR(32)) - 1
           IF (blk <= 0) blk=LEN_TRIM(zmeta)
@@ -1987,5 +2041,5 @@
           DO i=1,this%nrec
             j=j+1 ; IF (j>ms) j=1
-            zmeta = str_to_upper(meta(j))
+            zmeta = to_upper(meta(j))
             blk=INDEX(TRIM(zmeta),CHAR(32))-1 
             IF (blk <= 0) blk=LEN_TRIM(zmeta)
@@ -1994,5 +2048,5 @@
       END SELECT
     ELSE
-      zmeta = str_to_upper(TRIM(this%name))
+      zmeta = to_upper(TRIM(this%name))
       SELECT CASE(this%nrec)
         CASE(-3,-2,-1)
@@ -2223,5 +2277,5 @@
     !! - -7  : argument not found (i.e. does not set in the parser)
     !! - -19 : parsing not done yet
-    !! - -20 : (previous) parsing failed
+    !! - -20 : parsing failed
     !! - -21 : inconsistent destination type
     !! @note
@@ -2480,5 +2534,5 @@
   !! @param[in,out] this An argc object 
   !! @param[out] output A scalar with the first value of the argument
-  !! @return An errors::error object with -12 if the destination variable's type
+  !! @return An errors::error object with -21 if the destination variable's type
   !! is inconsistent, 0 otherwise.
   FUNCTION ac_get_dv_sc(this, output) RESULT(ret)
@@ -2492,5 +2546,5 @@
       !! Output value
     TYPE(error) :: ret
-      !! Error object with the -12 if the destination variable's type is inconsistent, 0 otherwise
+      !! Error object with the -21 if the destination variable's type is inconsistent, 0 otherwise
     ret = noerror
     IF (this%ptype /= ap_real) THEN
Index: trunk/LMDZ.TITAN/libf/muphytitan/asciiread.f90
===================================================================
--- trunk/LMDZ.TITAN/libf/muphytitan/asciiread.f90	(revision 1896)
+++ trunk/LMDZ.TITAN/libf/muphytitan/asciiread.f90	(revision 1897)
@@ -1,5 +1,5 @@
-! Copyright Jérémie Burgalat (2010-2015)
-! 
-! burgalat.jeremie@gmail.com
+! Copyright Jérémie Burgalat (2010-2015,2017)
+! 
+! jeremie.burgalat@univ-reims.fr
 ! 
 ! This software is a computer program whose purpose is to provide configuration 
@@ -32,8 +32,8 @@
 ! knowledge of the CeCILL-B license and that you accept its terms.
 
-!! file: asciiread.F90
+!! file: asciiread.f90
 !! summary: ASCII data file reader source file
 !! author: burgalat
-!! date: 2013-2015
+!! date: 2013-2015,2017
 MODULE ASCIIREAD
   !! ASCII data file reader module
@@ -42,5 +42,5 @@
   !! data array from ASCII file.
   !!
-  !! ```fortran 
+  !! ``` 
   !! FUNCTION read_data(path,data) RESULT(err)
   !! ```
@@ -65,5 +65,5 @@
   !!
   !! - path does not refer to a existing file (1)
-  !! - Logical unit 666 is not free (1)
+  !! - No free logical unit available (1)
   !! - the file does not have regular data-columns number (5)
   !! - at least a value cannot be cast in double precision (5)
@@ -79,5 +79,6 @@
   !! On success, the shape of the 3D output array will be _data(R,C,D)_.
   USE, INTRINSIC :: ISO_FORTRAN_ENV, ONLY: IOSTAT_END, IOSTAT_EOR
-  USE STRINGS, ONLY: tokenize,from_string, st_slen
+  !USE STRING_OP, ONLY: tokenize,from_string, st_slen
+  USE STRING_OP
   USE ERRORS
   IMPLICIT NONE
@@ -85,5 +86,5 @@
   PRIVATE
   PUBLIC :: noerror,error, error_to_string,aborting
-  PUBLIC :: read_data, OPERATOR(/=), OPERATOR(==)
+  PUBLIC :: readline,read_data, OPERATOR(/=), OPERATOR(==)
 
   !! Global interface to reading methods
@@ -110,5 +111,5 @@
     !! Error occured when:
     !!    - Path does not refer to a existing file (-11)
-    !!    - No free logical unit available (-12)
+    !!    - No free logical unit available (-1)
     !!    - The file does not have regular data-columns number (-5)
     !!    - At least a value cannot be cast in double precision (-10)
@@ -120,7 +121,4 @@
     !! On success, the shape of the 3D output array will be _output(R,C,D)_.
     !! On error, the 3D output array is __not allocated__.
-    !!
-    !! @note
-    !! The function uses the logical unit 666 !
     CHARACTER(len=*), INTENT(in)                             :: path      !! Path of the input data file 
     REAL(kind=8), INTENT(out), DIMENSION(:,:,:), ALLOCATABLE :: data3d    !! 3D-array with the output values (double precision)
@@ -130,5 +128,5 @@
     INTEGER                                           :: i,lc,tlc
     INTEGER                                           :: ndr,ndc,ndd
-    INTEGER                                           :: ir,jc,kd
+    INTEGER                                           :: ir,jc,kd,lu
     REAL(kind=8), DIMENSION(:), ALLOCATABLE           :: tmp
     CHARACTER(len=5)                                  :: slc
@@ -144,10 +142,8 @@
       err = error(trim(path)//": no such file",-1) ; RETURN
     ENDIF
-    INQUIRE(unit=666,OPENED=ok)
-    IF (ok) THEN
-      err = error("lun 666 is already used",-1) ; RETURN
-    ENDIF
+    lu = free_lun() 
+    IF (lu == -1) THEN ; err = error("Cannot find available logical unit...",-1) ; RETURN ; ENDIF
     ! Open file
-    OPEN(666,FILE=TRIM(path),STATUS='OLD',ACTION='READ')
+    OPEN(lu,FILE=TRIM(path),STATUS='OLD',ACTION='READ')
 
     ! First pass : 
@@ -158,5 +154,5 @@
     lc = 0 ; tlc = 0 
     ndr = -1 ; ndc = -1 ; ndd = 1 
-    DO WHILE(readline(666,line)) 
+    DO WHILE(readline(lu,line)) 
       lm1 = line
       ! Read the line
@@ -193,4 +189,6 @@
       ENDIF
       IF (ndc == -1) ndc = SIZE(wrds)
+      IF (ALLOCATED(wrds)) DEALLOCATE(wrds)
+      IF (ALLOCATED(tmp)) DEALLOCATE(tmp)
     ENDDO
 
@@ -203,9 +201,9 @@
 
     ! Rewind input data file
-    REWIND(666)
+    REWIND(lu)
     ! Allocate memory
     ALLOCATE(data3d(ndr,ndc,ndd))
     ir = 0 ; kd = 1 ; 
-    DO WHILE(readline(666,line)) 
+    DO WHILE(readline(lu,line)) 
       IF (INDEX(TRIM(ADJUSTL(line)),"#") == 1) CYCLE
       ir = ir + 1
@@ -216,6 +214,7 @@
       ok = tokenize(line,wrds,zdelim,.true.)
       DO jc = 1,ndc ; ok = from_string(wrds(jc),data3d(ir,jc,kd)) ; ENDDO
-    ENDDO
-    CLOSE(666)
+      IF (ALLOCATED(wrds)) DEALLOCATE(wrds)
+    ENDDO
+    CLOSE(lu)
   END FUNCTION read_data_3d
 
@@ -236,11 +235,10 @@
     !!
     !! - Path does not refer to a existing file (-1)
-    !! - Logical unit 666 is not free (-1)
+    !! - No free logical unit available (-1)
     !! - The file does not have regular data-columns number (-5)
     !! - At least a value cannot be cast in double precision (-5)
     !!
     !! On error, the 2D output array is __not allocated__.
-    !! @note
-    !! The function uses the logical unit 666 !
+    USE FSYSTEM
     CHARACTER(len=*), INTENT(in)                           :: path      !! Path of the input data file 
     REAL(kind=8), INTENT(out), DIMENSION(:,:), ALLOCATABLE :: data2d    !! 2D-array with the output values (double precision)
@@ -249,22 +247,18 @@
     LOGICAL                                           :: ok
     INTEGER                                           :: i,e,vc,lc
-    INTEGER                                           :: nl,nc
+    INTEGER                                           :: nl,nc,lu
     REAL(kind=8), DIMENSION(:), ALLOCATABLE           :: tmp
     CHARACTER(len=5)                                  :: slc
     CHARACTER(len=:), ALLOCATABLE                     :: line,zdelim
     CHARACTER(len=st_slen), DIMENSION(:), ALLOCATABLE :: wrds
-
     zdelim = CHAR(9)//CHAR(32)
     IF (PRESENT(delimiter)) zdelim = delimiter
-    ! Gets array size
     INQUIRE(FILE=TRIM(path),EXIST=ok)
     IF (.NOT.ok) THEN
       err = error(trim(path)//": no such file",-1) ; RETURN
     ENDIF
-    INQUIRE(unit=666,OPENED=ok)
-    IF (ok) THEN
-      err = error("lun 666 is already used",-1) ; RETURN
-    ENDIF
-    OPEN(666,FILE=TRIM(path),STATUS='OLD',ACTION='READ')
+    lu = free_lun() 
+    IF (lu == -1) THEN ; err = error("Cannot find available logical unit...",-1) ; RETURN ; ENDIF
+    OPEN(lu,FILE=TRIM(path),STATUS='OLD',ACTION='READ')
     vc = 0 ; lc=0 ; ok = .true.
     ! Read the file twice :)
@@ -273,5 +267,5 @@
     DO 
       ! Read the line
-      IF (.NOT.readline(666,line)) EXIT
+      IF (.NOT.readline(lu,line)) EXIT
       lc = lc + 1 ; WRITE(slc,'(I5)') lc ; slc = ADJUSTL(slc)
       ! skip empty/comment line
@@ -279,5 +273,4 @@
       ! update row counter
       vc = vc + 1
-      ! Splits line in words
       IF (.NOT.tokenize(line,wrds,zdelim,.true.)) THEN 
         ! cannot tokenize
@@ -286,9 +279,7 @@
       ELSEIF (.NOT.from_string(wrds,tmp)) THEN
         ! cannot cast values
-        do i=1,size(wrds) ; write(*,*) trim(wrds(i)) ; enddo
-        write(1111,'(a)') line
         err = error(trim(path)//": Cannot cast values at line "//TRIM(slc),-5)
         RETURN
-      ELSEIF (nc > 0 .AND. nc /= SIZE(wrds)) THEN 
+      ELSEIF (nc > 0 .AND. nc /= SIZE(tmp)) THEN 
         ! current number of columns not equal to last one
         err = error(trim(path)//": Invalid number of columns (line "//TRIM(slc)//")",-5)
@@ -296,7 +287,9 @@
       ENDIF
       IF (nc == -1) nc = SIZE(wrds)
+      IF (ALLOCATED(wrds)) DEALLOCATE(wrds)
+      IF (ALLOCATED(tmp)) DEALLOCATE(tmp)
     ENDDO
     ! Rewind input data file
-    REWIND(666)
+    REWIND(lu)
     nl = vc
     ! allocate memory
@@ -306,5 +299,5 @@
     DO WHILE(vc <= nl)
       ! Reads the line
-      IF (.NOT.readline(666,line)) EXIT
+      IF (.NOT.readline(lu,line)) EXIT
       ! Check if we have comment or null string
       IF (INDEX(TRIM(ADJUSTL(line)),"#") == 1.OR.LEN_TRIM(line) == 0) CYCLE
@@ -314,6 +307,7 @@
         ok = from_string(wrds(i),data2d(vc,i))
       ENDDO
-    ENDDO
-    CLOSE(666)
+      IF (ALLOCATED(wrds)) DEALLOCATE(wrds)
+    ENDDO
+    CLOSE(lu)
     RETURN
   END FUNCTION read_data_2d
@@ -327,5 +321,5 @@
     !! The function is intended to read a file line by line:
     !!
-    !! ```fortran
+    !! ```
     !! lu = 1
     !! open(lu,file="path/to/the/file/to/read")
@@ -358,3 +352,3 @@
   END FUNCTION readline
 
-END MODULE
+END MODULE ASCIIREAD
Index: trunk/LMDZ.TITAN/libf/muphytitan/cfgparse.F90
===================================================================
--- trunk/LMDZ.TITAN/libf/muphytitan/cfgparse.F90	(revision 1896)
+++ trunk/LMDZ.TITAN/libf/muphytitan/cfgparse.F90	(revision 1897)
@@ -1,21 +1,21 @@
-! Copyright Jérémie Burgalat (2010-2015)
-! 
-! burgalat.jeremie@gmail.com
-! 
-! This software is a computer program whose purpose is to provide configuration 
+! Copyright Jérémie Burgalat (2010-2015,2017)
+!
+! jeremie.burgalat@univ-reims.fr
+!
+! This software is a computer program whose purpose is to provide configuration
 ! file and command line arguments parsing features to Fortran programs.
-! 
+!
 ! This software is governed by the CeCILL-B license under French law and
-! abiding by the rules of distribution of free software.  You can  use, 
+! abiding by the rules of distribution of free software.  You can  use,
 ! modify and/ or redistribute the software under the terms of the CeCILL-B
 ! license as circulated by CEA, CNRS and INRIA at the following URL
-! "http://www.cecill.info". 
-! 
+! "http://www.cecill.info".
+!
 ! As a counterpart to the access to the source code and  rights to copy,
 ! modify and redistribute granted by the license, users are provided only
 ! with a limited warranty  and the software's author,  the holder of the
 ! economic rights,  and the successive licensors  have only  limited
-! liability. 
-! 
+! liability.
+!
 ! In this respect, the user's attention is drawn to the risks associated
 ! with loading,  using,  modifying and/or developing or reproducing the
@@ -25,15 +25,15 @@
 ! professionals having in-depth computer knowledge. Users are therefore
 ! encouraged to load and test the software's suitability as regards their
-! requirements in conditions enabling the security of their systems and/or 
-! data to be ensured and,  more generally, to use and operate it in the 
-! same conditions as regards security. 
-! 
+! requirements in conditions enabling the security of their systems and/or
+! data to be ensured and,  more generally, to use and operate it in the
+! same conditions as regards security.
+!
 ! The fact that you are presently reading this means that you have had
 ! knowledge of the CeCILL-B license and that you accept its terms.
 
 !! file: cfgparse.F90
-!! summary: Configuration file parser source file
-!! author: burgalat
-!! date: 2013-2015, 2017
+!! summary: Configuration file parser source file.
+!! author: J. Burgalat
+!! date: 2013-2015,2017
 
 #include "defined.h"
@@ -41,11 +41,14 @@
 MODULE CFGPARSE
   !! Configuration file parsing module
-  !!  
-  !! This module defines a set of derived types as well as methods to parse configuration files. 
+  !!
+  !! This module defines a set of derived types as well as methods to parse configuration files.
+  !!
+  !! If you only wish to have an overview of cfgparse usage, you'd better go
+  !! [here](|url|/page/swift/p02_cfgparse.html).
   !! @todo
-  !! Add interpolation from environment and/or parser options
+  !! Add interpolation from environment and/or parser options.
   USE, INTRINSIC :: ISO_FORTRAN_ENV
   USE ERRORS
-  USE STRINGS
+  USE STRING_OP
   USE FSYSTEM
   IMPLICIT NONE
@@ -56,4 +59,5 @@
             cfg_get_value, cfg_set_value, cfg_count, cfg_check_name, &
             cfg_has_option, cfg_has_section, &
+            cfg_option_names,cfg_section_names, &
             cfg_remove_option, cfg_remove_section, &
             cfg_sort_options
@@ -63,5 +67,5 @@
   ! some public definitions from other modules
   ! from strings
-  PUBLIC :: str_to_lower,st_slen, st_llen
+  PUBLIC :: to_lower,st_slen, st_llen
 
   PUBLIC :: OPERATOR(==), OPERATOR(/=), ASSIGNMENT(=)
@@ -76,6 +80,6 @@
   TYPE, PUBLIC :: cfgparser
     !! Define a parser of options
-    !! 
-    !! A [[cfgparser(type)]] stores [[option(type)]] objects. 
+    !!
+    !! A [[cfgparser(type)]] stores [[option(type)]] objects.
     TYPE(option), DIMENSION(:), ALLOCATABLE  :: options !! list of options.
 #if HAVE_FTNPROC
@@ -124,7 +128,7 @@
     PROCEDURE, PUBLIC :: remove_option  => cfg_remove_option
     !> Remove a section (and all the associated options) from the parser.
-    PROCEDURE, PUBLIC :: remove_section => cfg_remove_section 
-    !> Get value(s) of an option in the parser by name 
-    !! 
+    PROCEDURE, PUBLIC :: remove_section => cfg_remove_section
+    !> Get value(s) of an option in the parser by name
+    !!
     !! ```
     !! FUNCTION cfg_get_value(this,name,output) RESULT(error)
@@ -135,8 +139,8 @@
     !! On error, __output__ argument is undefined (that is, left unchanged
     !! for scalar versions, **unallocated** for vector version).
-    !! 
+    !!
     !! Errors occur in the following situations:
     !! - The option has no value (-6)
-    !! - The option does not exist (-7) 
+    !! - The option does not exist (-7)
     !! - The option's value cannot be cast in the desired type (-10)
     GENERIC, PUBLIC :: get_value     => cp_get_rv_sc,cp_get_dv_sc,cp_get_iv_sc, &
@@ -144,6 +148,6 @@
                                         cp_get_rv_ve,cp_get_dv_ve,cp_get_iv_ve, &
                                         cp_get_lv_ve,cp_get_cv_ve,cp_get_sv_ve
-    !> Set value(s) of an option in the parser by name 
-    !! 
+    !> Set value(s) of an option in the parser by name
+    !!
     !! ```
     !! FUNCTION cfg_set_value(this,name,input,create) RESULT(error)
@@ -167,6 +171,6 @@
   END TYPE cfgparser
 
-  !> Get value(s) of an option in the parser by name. 
-  !! 
+  !> Get value(s) of an option in the parser by name.
+  !!
   !! ```
   !! FUNCTION cfg_get_value(parser,name,output) RESULT(error)
@@ -177,8 +181,8 @@
   !! On error, __output__ argument is undefined (that is, left unchanged
   !! for scalar versions, **unallocated** for vector version).
-  !! 
+  !!
   !! Errors occur in the following situations:
   !! - The option has no value (-6)
-  !! - The option does not exist (-7) 
+  !! - The option does not exist (-7)
   !! - The option's value cannot be cast in the desired type (-10)
   INTERFACE cfg_get_value
@@ -189,6 +193,6 @@
   END INTERFACE
 
-    !> Set value(s) of an option in the parser by name 
-    !! 
+    !> Set value(s) of an option in the parser by name
+    !!
     !! ```
     !! FUNCTION set_value(this,name,input,create) RESULT(error)
@@ -201,13 +205,13 @@
     !! exist in _this_ parser.
     !! @warning
-    !! In such case, if the given is not valid, an assertion is raised !
+    !! In such case, if the given __name__ is not valid, an error is raised !
     !!
     !! On error (i.e. no option matches the given _name_), no values are set.
-    INTERFACE cfg_set_value 
+    INTERFACE cfg_set_value
       MODULE PROCEDURE :: cp_set_rv_sc,cp_set_dv_sc,cp_set_iv_sc, &
                           cp_set_lv_sc,cp_set_cv_sc,cp_set_sv_sc, &
                           cp_set_rv_ve,cp_set_dv_ve,cp_set_iv_ve, &
                           cp_set_lv_ve,cp_set_cv_ve,cp_set_sv_ve
-    END INTERFACE 
+    END INTERFACE
 
     !> Derived type assignment operator
@@ -230,16 +234,16 @@
     this%section = other%section
     this%values = other%values
-  END SUBROUTINE op_affect_sc 
-  
+  END SUBROUTINE op_affect_sc
+
   FUNCTION op_valid(opt) RESULT(ok)
     !! Check whether or not the option is valid (i.e. has name)
-    TYPE(option), INTENT(in)      :: opt  !! An option object 
+    TYPE(option), INTENT(in)      :: opt  !! An option object
     LOGICAL :: ok !! True if the option is valid, false otherwise.
     ok = LEN_TRIM(opt%name) > 0
   END FUNCTION op_valid
 
-  SUBROUTINE op_clear(opt) 
+  SUBROUTINE op_clear(opt)
     !! Clear and invalid the given option.
-    TYPE(option), INTENT(inout)      :: opt  !! An option object 
+    TYPE(option), INTENT(inout)      :: opt  !! An option object
     opt%name = ''
     opt%section = ''
@@ -261,45 +265,71 @@
     ENDIF
   END FUNCTION op_full_name
- 
+
   FUNCTION op_split_name(fname,sname,pname) RESULT(err)
     !> Split a full name in section and option names
-    !! 
-    !! The method splits a full name into (section,option) names. Output names (if any) are always
-    !! set to lower case. 
+    !!
+    !! The method splits a full name into (section,option) names:
+    !!
+    !! - Option basename is always set in lower-case.
+    !! - If any, section name case is left unmodified.
     !!
     !! A full name simply consists in a section name and an option name separated by a single "/".
     !!
-    !! The method never checks the validity of the output names. Consider using [[cfg_check_name(function)]] 
-    !! to do so. 
-    !! @note 
-    !! If _fname_ does not contains any "/", the method set the special name "\_\_default\_\_" for the output
-    !! section name. 
+    !! The method never checks the validity of the output names. Consider using [[cfg_check_name(function)]]
+    !! to do so.
     !! @note
-    !! On success, option and section names are set to lower case. Otherwise they are set to empty strings.
+    !! If _fname_ does not contains any "/", the method sets the special name "\_\_default\_\_" for the output
+    !! section name.
+    !! @note
+    !! On failure, output arguments are set to empty strings.
     !! @warning
-    !! If _fname_ ends with a "/", an error (-6, invalid name) is raised: the method always assumes it can
+    !! If _fname_ ends with a "/", an error (-9, invalid name) is raised: the method always assumes it can
     !! find an option part in the name.
     CHARACTER(len=*), INTENT(in)               :: fname    !! A name to split
     CHARACTER(len=:), INTENT(out), ALLOCATABLE :: sname, & !! Section part of the name
                                                   pname    !! Option part of the name
-    TYPE(error) :: err !! Error status of the method 
-    INTEGER     :: idx
+    TYPE(error)                                :: err      !! Error status of the method
+    INTEGER                       :: idx
+    CHARACTER(len=:), ALLOCATABLE :: tfname
     err = noerror ; pname = "" ; sname = ""
+    tfname = op_format(fname,sname,pname)
+    IF (LEN_TRIM(tfname) == 0) err = error("Invalid option name ("//TRIM(fname)//")",-9)
+  END FUNCTION op_split_name
+
+  FUNCTION op_format(name,sname,pname) RESULT(oname)
+    !! Format the input name to be consistent with character case requirements.
+    !!
+    !! Given a **name**, the method tries to split in section/option names.
+    !! Then it converts the option part in lower-case.
+    !!
+    !! If no section part is found (not '/' or set as first character of **name**), the
+    !! special section name `__default__` is set.
+    !!
+    !! If **name** ends with a '/', it is an error and the method returns an empty string.
+    CHARACTER(len=*), INTENT(in)  :: name  !! Name to format.
+    CHARACTER(len=:), ALLOCATABLE, INTENT(out), OPTIONAL :: sname !! Section part of the name (optional output)
+    CHARACTER(len=:), ALLOCATABLE, INTENT(out), OPTIONAL :: pname !! Option part of the name (optional output)
+    CHARACTER(len=:), ALLOCATABLE :: oname                        !! Formatted full option name.
+    INTEGER                       :: idx
+    CHARACTER(len=:), ALLOCATABLE :: zsname,zpname
+    zpname = "" ; zsname = ""
     ! splits input name in sname, pname
-    idx = INDEX(fname,'/')
-    IF (idx == LEN_TRIM(fname)) THEN
-      err = error("Invalid option name ("//TRIM(fname)//")",-9)
+    idx = INDEX(name,'/')
+    IF (idx == LEN_TRIM(name)) THEN
+      oname = ''
+      IF (PRESENT(sname)) sname = ''
+      IF (PRESENT(pname)) pname = ''
       RETURN
     ELSE IF (idx <= 1) THEN
-      sname = "__default__" ; pname = TRIM(fname)
-      IF (idx == 1) pname=pname(2:)
-    ELSE
-      sname = fname(:idx-1)
-      pname = fname(idx+1:LEN_TRIM(fname))
-    ENDIF
-    ! 17/12/2014: set option name to lower 
-    pname = str_to_lower(pname)
-    sname = str_to_lower(sname)
-  END FUNCTION op_split_name
+      zsname = "__default__" ; zpname = to_lower(TRIM(name))
+      IF (idx == 1) zpname=zpname(2:)
+    ELSE
+      zsname = name(:idx-1)
+      zpname = to_lower(name(idx+1:LEN_TRIM(name)))
+    ENDIF
+    oname = zsname//"/"//zpname
+    IF (PRESENT(sname)) sname = zsname
+    IF (PRESENT(pname)) pname = zpname 
+  END FUNCTION op_format
 
   FUNCTION op_greater_than(left,right) RESULT(ret)
@@ -309,5 +339,5 @@
     TYPE(option), INTENT(in) :: left  !! LHS option.
     TYPE(option), INTENT(in) :: right !! RHS option.
-    LOGICAL :: ret 
+    LOGICAL :: ret
       !! .true. if LHS is _greater_ than RHS (based on section and option name)
     ret = LGT(op_full_name(left),op_full_name(right))
@@ -320,5 +350,5 @@
     TYPE(option), INTENT(in) :: left  !! LHS option.
     TYPE(option), INTENT(in) :: right !! RHS option.
-    LOGICAL :: ret 
+    LOGICAL :: ret
       !! .true. if LHS is _less_ than RHS (based on section and option name)
     ret = LLT(op_full_name(left),op_full_name(right))
@@ -327,7 +357,7 @@
   FUNCTION op_to_str(opt,num_values) RESULT(str)
     !! Get the string representation of a option object
-    !! @note 
+    !! @note
     !! If the object is not valid an empty string is returned.
-    TYPE(option), INTENT(in) :: opt 
+    TYPE(option), INTENT(in) :: opt
       !! A option object
     INTEGER, INTENT(in), OPTIONAL :: num_values
@@ -343,5 +373,5 @@
     str = TRIM(opt%name)//" = " ; np = LEN(str)
     ALLOCATE(CHARACTER(len=np) :: nspcs) ; nspcs(1:) = " "
-    ! stores the error but do not check... 
+    ! stores the error but do not check...
     ret = words_to_vector(opt%values,vec)
     IF (.NOT.ALLOCATED(vec)) RETURN
@@ -382,13 +412,22 @@
 
   FUNCTION cfg_check_name(name) RESULT(valid)
-    !! Check if a name is valid
-    !!
-    !! A valid option/section name begins with a letter and is followed by any 
-    !! number of alphanumeric characters and underscore (`[A-Za-z][A-Za-z0-9\_]\*`).
+    !! Check if a name is valid.
+    !!
+    !! If **name** contains a '/' it is assumed to be a full option name. In such case
+    !! both parts of the name are checked against section/option names requirements (see below).
+    !!
+    !! Otherwise it is assumed to be the basename of the option.
+    !! 
+    !! A valid option (base) name is an alphanumeric sequence in lower-case that always begin by
+    !! a letter.
+    !!
+    !! A valid section name is and alphanumeric sequence (in any case) that always begins by
+    !! by a letter.
     CHARACTER(len=*), INTENT(in) :: name !! A string with the name to check.
     LOGICAL :: valid                     !! .true. if the name is valid, .false. otherwise
     INTEGER                       :: i
-    CHARACTER(len=26), PARAMETER  :: alpha = "abcdefghijklmnopqrstuvwxyz"
-    CHARACTER(len=12), PARAMETER  :: num   = "0123456789_"
+    CHARACTER(len=26), PARAMETER  :: alpha  = "abcdefghijklmnopqrstuvwxyz"
+    CHARACTER(len=26), PARAMETER  :: ualpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+    CHARACTER(len=12), PARAMETER  :: num    = "0123456789_"
     CHARACTER(len=:), ALLOCATABLE :: pname,sname
     TYPE(error)                   :: err
@@ -401,5 +440,5 @@
       ENDIF
     ELSE
-      pname = str_to_lower(TRIM(name))
+      pname = to_lower(TRIM(name))
       sname = "__default__"
     ENDIF
@@ -416,19 +455,34 @@
       i = INDEX(sname,CHAR(32))
       IF (i /= 0.OR.LEN_TRIM(sname) <= 0) RETURN
-      valid = (VERIFY(sname(1:1),alpha) == 0 .AND.VERIFY(TRIM(sname),alpha//"/"//num) == 0)
+      valid = (VERIFY(sname(1:1),ualpha//alpha) == 0 .AND.VERIFY(TRIM(sname),ualpha//alpha//num) == 0)
     ENDIF
   END FUNCTION cfg_check_name
 
-  FUNCTION cfg_count(this) RESULT(num)
+  FUNCTION cfg_count(this,section) RESULT(num)
     !! Get the total number of option in the parser.
     !!
+    !! If a section name is given in argument, the method returns the count for the given section only.
+    !!
+    !! To get the number of top-level options (i.e. that belongs to the default section) the keyword \_\_default\_\_
+    !! should be set for the section argument.
+    !!
+    !! If __section__ is not defined in the parser, the method returns 0.
+    !!
     !! @internal
-    !! If no options are defined, then it implies that the internal vector of options is 
+    !! If no options are defined, then it implies that the internal vector of options is
     !! not allocated.
-    OBJECT(cfgparser), INTENT(in) :: this !! A cfgparser object to search in 
-    INTEGER :: num                        !! Number of current options registered in the parser.
+    OBJECT(cfgparser), INTENT(in)          :: this    !! A cfgparser object to search in
+    CHARACTER(len=*), INTENT(in), OPTIONAL :: section !! Optional section name to search in.
+    INTEGER :: num                                    !! Number of current options registered in the parser.
+    INTEGER :: i
     num = 0
     IF(.NOT.ALLOCATED(this%options)) RETURN
-    num = SIZE(this%options)
+    IF (.NOT.PRESENT(section)) THEN
+      num = SIZE(this%options)  
+    ELSE
+      DO i=1, SIZE(this%options)
+        IF (this%options(i)%section == section) num = num+1
+      ENDDO
+    ENDIF
   END FUNCTION cfg_count
 
@@ -469,5 +523,5 @@
         ! Found a match so start looking again
         found = (tmp(j) == this%options(i)%section .OR. &
-                 this%options(i)%section == "__default__") 
+                 this%options(i)%section == "__default__")
         IF (found) EXIT
       ENDDO
@@ -482,37 +536,58 @@
   END FUNCTION cfg_section_names
 
-  FUNCTION cfg_option_names(this) RESULT(list)
+  FUNCTION cfg_option_names(this,secname) RESULT(list)
     !! Get the list of option names.
     !!
     !! @note
     !! If the parser does not have options, the vector is still allocated but with 0 elements.
-    OBJECT(cfgparser), INTENT(in) :: this                     !! A cfgparser object to process. 
-    CHARACTER(len=st_slen), DIMENSION(:), ALLOCATABLE :: list !! List of option names. 
-    INTEGER               :: i,no
+    OBJECT(cfgparser), INTENT(in)                     :: this    !! A cfgparser object to process.
+    CHARACTER(len=*), INTENT(in), OPTIONAL            :: secname !! Optional section name to search in.
+    CHARACTER(len=st_slen), DIMENSION(:), ALLOCATABLE :: list    !! List of option names.
+    INTEGER               :: j,i,no,nso
     no = cfg_count(this)
-    ALLOCATE(list(no))
-    IF (no == 0) RETURN
-    DO i=1,no
-      IF (this%options(i)%section == "__default__") THEN
-        list(i) = TRIM(this%options(i)%name)
-      ELSE
-        list(i) = TRIM(this%options(i)%section)//"/"//TRIM(this%options(i)%name)
-      ENDIF
-    ENDDO
+    IF (no == 0) THEN
+       ALLOCATE(list(no)) ; RETURN
+    ENDIF
+    IF (PRESENT(secname)) THEN
+      IF (.NOT.cfg_has_section(this,TRIM(secname))) THEN
+        ALLOCATE(list(no)) ; RETURN
+      ELSE
+        nso = 0
+        DO i=1,no ; IF (this%options(i)%section == TRIM(secname)) nso = nso + 1 ; ENDDO
+        ALLOCATE(list(nso))
+        IF (nso == 0) RETURN
+        j = 1
+        DO i=1,no
+          IF (this%options(i)%section == TRIM(secname)) THEN
+            list(j) = TRIM(this%options(i)%section)//"/"//TRIM(this%options(i)%name) ; j=j+1
+          ENDIF
+        ENDDO
+      ENDIF
+    ELSE
+      ALLOCATE(list(no))
+      DO i=1,no
+        IF (this%options(i)%section == "__default__") THEN
+          list(i) = TRIM(this%options(i)%name)
+        ELSE
+          list(i) = TRIM(this%options(i)%section)//"/"//TRIM(this%options(i)%name)
+        ENDIF
+      ENDDO
+    ENDIF
   END FUNCTION cfg_option_names
 
   FUNCTION cfg_has_section(this,name) RESULT(yes)
     !! Check if parser has section by name
+    !!
+    !! @note
+    !! Keep in mind that section name in the configuration are case-sensitive.
     OBJECT(cfgparser), INTENT(in) :: this !! cfgparser object
     CHARACTER(len=*), INTENT(in)  :: name !! Name of the section to search
     LOGICAL :: yes                        !! .true. if the section exists, .false. otherwise
-    CHARACTER(len=:), ALLOCATABLE :: zname
     INTEGER                       :: i,no
     yes = .false.
     no = cfg_count(this)
     IF (no == 0) RETURN
-    zname = str_to_lower(name)
     DO i = 1,no
-      IF (this%options(i)%section == zname) THEN
+      IF (this%options(i)%section == name) THEN
         yes = .true.
         RETURN
@@ -527,15 +602,12 @@
     LOGICAL :: yes                        !! .true. if the option is found, .false. otherwise
     CHARACTER(len=:), ALLOCATABLE :: pname,zname
-    INTEGER                       :: i,no
+    INTEGER                       :: i,no,iscan
     yes = .false.
     no = cfg_count(this)
     IF (no == 0) RETURN
-    IF (SCAN(name,"/") <= 0) THEN
-      zname = "__default__"//"/"//str_to_lower(TRIM(name))
-    ELSE
-      zname = str_to_lower(TRIM(name))
-    ENDIF
+    zname = op_format(name)
+    IF (LEN_TRIM(zname) == 0) RETURN
     DO i = 1,no
-      pname = op_full_name(this%options(i)) 
+      pname = op_full_name(this%options(i))
       IF (pname == zname) THEN
         yes = .true.
@@ -545,5 +617,5 @@
   END FUNCTION cfg_has_option
 
-  SUBROUTINE cfg_sort_options(this) 
+  SUBROUTINE cfg_sort_options(this)
     !! Sort the options in the parser (alphabetiCALLy).
     OBJECT(cfgparser), INTENT(inout) :: this !! A cfgparser object
@@ -552,21 +624,15 @@
     IF (no == 0) RETURN
     CALL insertionSort(this%options)
-  END SUBROUTINE cfg_sort_options 
+  END SUBROUTINE cfg_sort_options
 
   SUBROUTINE cfg_remove_option(this,name)
     !! Remove an option from parser by name.
-    OBJECT(cfgparser), INTENT(inout) :: this !! A cfgparser object to search in 
+    OBJECT(cfgparser), INTENT(inout) :: this !! A cfgparser object to search in
     CHARACTER(len=*), INTENT(in)     :: name !! The name of the option to remove
-    CHARACTER(len=:), ALLOCATABLE    :: zname,pname
     INTEGER                                 :: no,idx,i,j
     TYPE(option), DIMENSION(:), ALLOCATABLE :: tmp
-    IF (SCAN(name,"/") <= 0) THEN
-      zname = "__default__"//"/"//str_to_lower(TRIM(name))
-    ELSE
-      zname = str_to_lower(TRIM(name))
-    ENDIF
-    idx = cp_get_opt_idx(this,zname)
+    idx = cp_get_opt_idx(this,name)
+    IF (idx == -1) RETURN
     no = cfg_count(this)
-    IF (idx == -1) RETURN
     ! only one opt
     IF (no == 1) THEN
@@ -589,5 +655,5 @@
       this%options(i) = tmp(i)
       CALL op_clear(tmp(i))
-    ENDDO 
+    ENDDO
     DEALLOCATE(tmp)
   END SUBROUTINE cfg_remove_option
@@ -597,21 +663,18 @@
     !!
     !! The method removes all the options that belong to the given section name.
-    OBJECT(cfgparser), INTENT(inout) :: this 
-      !! A cfgparser object to search in 
+    OBJECT(cfgparser), INTENT(inout) :: this
+      !! A cfgparser object to search in
     CHARACTER(len=*), INTENT(in)     :: name
       !! The name of the section to remove
-    CHARACTER(len=:), ALLOCATABLE           :: zname
     INTEGER                                 :: no,i,j,icount
     INTEGER, DIMENSION(:), ALLOCATABLE      :: idxs,itmp
     TYPE(option), DIMENSION(:), ALLOCATABLE :: tmp
-     
     no = cfg_count(this)
     IF (no == 0) RETURN
-    zname = str_to_lower(TRIM(name))
     ALLOCATE(itmp(no))
     itmp(:) = -1
     icount = 0
     DO i=1,no
-      IF (TRIM(this%options(i)%section) == zname) THEN
+      IF (TRIM(this%options(i)%section) == TRIM(name)) THEN
         itmp(icount+1) = i
         icount = icount + 1
@@ -627,5 +690,5 @@
        DEALLOCATE(this%options)
        RETURN
-    ENDIF 
+    ENDIF
     ALLOCATE(tmp(icount))
     j = 1
@@ -650,5 +713,5 @@
     !! Read configuration file
     !!
-    !! @note 
+    !! @note
     !! If the library support C bindings, the method can read included files which are defined
     !! by the __#include <...>__ directive (see [p_cfgparse](here) from more details).
@@ -662,13 +725,13 @@
       !! An error with the first error encountered
     INTEGER                       :: i
-    LOGICAL                       :: zoverride,ok 
-    TYPE(words)                   :: incfiles 
+    LOGICAL                       :: zoverride,ok
+    TYPE(words)                   :: incfiles
     CHARACTER(len=:), ALLOCATABLE :: name
     CHARACTER(len=st_slen)        :: isec
-    err = noerror 
+    err = noerror
     zoverride = .false. ; IF (PRESENT(override)) zoverride = override
     isec = "__default__"
     name = TRIM(path)
-    i = INDEX(name,'/',.true.) ; IF (i /= 0) name = name(i+1:) 
+    i = INDEX(name,'/',.true.) ; IF (i /= 0) name = name(i+1:)
     IF (i == 0) THEN
       name = fs_realpath("./"//path)
@@ -710,5 +773,5 @@
     no = cfg_count(this)
     IF (no == 0) THEN
-      err = error("No options to write",-7) 
+      err = error("No options to write",-7)
       RETURN
     ENDIF
@@ -728,5 +791,5 @@
       WRITE(lu,'(a)') op_to_str(this%options(i),nv)
     ENDDO
-  END FUNCTION cfg_write_config 
+  END FUNCTION cfg_write_config
 
   ! internal (private methods)
@@ -751,5 +814,5 @@
   FUNCTION cp_get_opt_idx(this,name) RESULT(idx)
     !! Get the index of an option by name in the parser.
-    !! 
+    !!
     !! The method searches in the parser for the option with the given (full) __name__.
     !! If found, it returns the index of the option in the internal vector of options. Otherwise
@@ -763,9 +826,6 @@
     no = cfg_count(this)
     IF (no == 0) RETURN
-    IF (SCAN(name,"/") <= 0) THEN
-      zname = "__default__"//"/"//str_to_lower(TRIM(name))
-    ELSE
-      zname = str_to_lower(TRIM(name))
-    ENDIF
+    zname = op_format(name) ! prepare the name to search.
+    IF (LEN_TRIM(zname) == 0) RETURN
     DO i=1,no
       pname = op_full_name(this%options(i))
@@ -780,27 +840,28 @@
     !! Update an option in the parser.
     !!
-    !! The attempts to update the option in the parser that matches __opt__ name.
-    !! 
-    !! If __name__ is given it is used instead of __opt__ name.
-    !!
-    !! If no option is found, __opt__ is appended in the parser. Otherwise the matched
-    !! option is updated (i.e. its values are set to __opt__ values).
-    !!
-    !! If the option is not valid, the method does nothing and -X error status is returned.
+    !! The method attempts to update the option in the parser.
+    !!
+    !! If __sname__ is set to empty string, the method searches for the option
+    !! in the default section.
+    !!
+    !! If no option is found, The the option is appended in the parser. Otherwise it is updated
+    !! with the content of __values__.
+    !!
+    !! If the option name is not valid, the method does nothing and -9 error status is returned.
     !!
     !! @internal
     !! The method performs the same kind of operations than the setters except that it
-    !! expects raw data ([[strings(module):words(type)]]).
+    !! expects raw data ([[string_op(module):words(type)]]).
     OBJECT(cfgparser), INTENT(inout) :: this   !! cfgparser object to process.
     CHARACTER(len=*), INTENT(in)     :: sname  !! Name of the section.
-    CHARACTER(len=*), INTENT(in)     :: pname  !! Name of the option.
+    CHARACTER(len=*), INTENT(in)     :: pname  !! Basename of the option.
     TYPE(words), INTENT(in)          :: values !! Raw values.
-    TYPE(error) :: err !! Error status.
+    TYPE(error)                      :: err    !! Error status.
     CHARACTER(len=:), ALLOCATABLE :: zsname,fname
     INTEGER                       :: i
     err = noerror
-    zsname = str_to_lower(TRIM(sname))
-    IF (LEN_TRIM(zsname) == 0 ) zsname = "__default__"
-    fname = zsname//"/"//str_to_lower(TRIM(pname))
+    zsname = TRIM(sname)
+    IF (LEN_TRIM(sname) == 0) zsname = "__default__"
+    fname = zsname//"/"//to_lower(TRIM(pname))
     IF (.NOT.cfg_check_name(fname)) THEN
        err = error("Invalid option (no name)",-9)
@@ -816,11 +877,8 @@
   END FUNCTION cp_update_opt
 
-
-  FUNCTION cp_add_opt(this,sname,pname,values) RESULT(err) 
+  FUNCTION cp_add_opt(this,sname,pname,values) RESULT(err)
     !! Add an option to the parser.
     !!
     !! In order to add an option to the default section, _sname_ should be left empty or set to "\_\_default\_\_".
-    !! 
-    !! If given, _opt_ points to the new option on output. If an error occured the pointer is null.
     !!
     !! The following error code can be returned:
@@ -828,10 +886,10 @@
     !!  - -8, the option already exists.
     !!  - -9, option name is not valid.
-    OBJECT(cfgparser), INTENT(inout) :: this   
+    OBJECT(cfgparser), INTENT(inout) :: this
       !! A cfgparser object to process.
     CHARACTER(len=*), INTENT(in)     :: sname
       !! Section name.
     CHARACTER(len=*), INTENT(in)     :: pname
-      !! Option name.
+      !! Option basename.
     TYPE(words), INTENT(in)          :: values
       !! Values to set.
@@ -839,8 +897,7 @@
       !! Return error status.
     CHARACTER(len=:), ALLOCATABLE           :: zsname,fname
-    TYPE(option), DIMENSION(:), ALLOCATABLE :: tmp 
+    TYPE(option), DIMENSION(:), ALLOCATABLE :: tmp
     INTEGER                                 :: no,i
-
-    TYPE(option) :: sca 
+    TYPE(option)                            :: sca
 
     err = noerror
@@ -848,5 +905,5 @@
     no = cfg_count(this)
     IF (LEN_TRIM(zsname) == 0) zsname = "__default__"
-    fname = TRIM(zsname)//"/"//TRIM(pname)
+    fname = TRIM(zsname)//"/"//to_lower(TRIM(pname))
     ! check name
     IF (.NOT.cfg_check_name(fname)) THEN
@@ -864,5 +921,5 @@
     ! build option
     CALL op_clear(sca)
-    sca%name = pname
+    sca%name = to_lower(TRIM(pname))
     sca%section = zsname
     sca%values = values
@@ -874,12 +931,12 @@
       ! parser has options: increase this%options size (ugly copy).
       ALLOCATE(tmp(no))
-      DO i =1,no 
-        tmp(i) = this%options(i) 
+      DO i =1,no
+        tmp(i) = this%options(i)
         CALL op_clear(this%options(i))
       ENDDO
       DEALLOCATE(this%options)
       ALLOCATE(this%options(no+1))
-      DO i =1,no 
-        this%options(i) = tmp(i) 
+      DO i =1,no
+        this%options(i) = tmp(i)
         CALL op_clear(tmp(i))
       ENDDO
@@ -898,10 +955,10 @@
     !!  - -6, the option does not have value(s).
     !!  - -10, the value cannot be converted in the output type.
-    OBJECT(cfgparser), INTENT(in) :: this   !! Cfgparser object 
+    OBJECT(cfgparser), INTENT(in) :: this   !! Cfgparser object
     CHARACTER(len=*), INTENT(in)  :: name   !! (Full) Name of the option to get
     REAL(kind=4), INTENT(out)     :: output !! Output value
-    TYPE(error) :: err 
+    TYPE(error) :: err
       !! Error status
-    INTEGER :: idx 
+    INTEGER :: idx
     CHARACTER(len=:), ALLOCATABLE :: tmp
     err = noerror
@@ -918,5 +975,5 @@
         err = error("Option "//TRIM(name)//" has no value",-6)
       ELSE
-        IF(.NOT.from_string(tmp,output)) & 
+        IF(.NOT.from_string(tmp,output)) &
         err = error(TRIM(name)//": Cannot convert "//tmp//" to real.",-10)
       ENDIF
@@ -931,10 +988,10 @@
     !!  - -6, the option does not have value(s).
     !!  - -10, the value cannot be converted in the output type.
-    OBJECT(cfgparser), INTENT(in) :: this   !! Cfgparser object 
+    OBJECT(cfgparser), INTENT(in) :: this   !! Cfgparser object
     CHARACTER(len=*), INTENT(in)  :: name   !! (Full) Name of the option to get
     REAL(kind=8), INTENT(out)     :: output !! Output value
-    TYPE(error) :: err 
+    TYPE(error) :: err
       !! Error status
-    INTEGER :: idx 
+    INTEGER :: idx
     CHARACTER(len=:), ALLOCATABLE :: tmp
     err = noerror
@@ -951,5 +1008,5 @@
         err = error("Option "//TRIM(name)//" has no value",-6)
       ELSE
-        IF(.NOT.from_string(tmp,output)) & 
+        IF(.NOT.from_string(tmp,output)) &
         err = error(TRIM(name)//": Cannot convert "//tmp//" to double.",-10)
       ENDIF
@@ -964,10 +1021,10 @@
     !!  - -6, the option does not have value(s).
     !!  - -10, the value cannot be converted in the output type.
-    OBJECT(cfgparser), INTENT(in) :: this   !! Cfgparser object 
+    OBJECT(cfgparser), INTENT(in) :: this   !! Cfgparser object
     CHARACTER(len=*), INTENT(in)  :: name   !! (Full) Name of the option to get
     INTEGER, INTENT(out)          :: output !! Output value
-    TYPE(error) :: err 
+    TYPE(error) :: err
       !! Error status
-    INTEGER :: idx 
+    INTEGER :: idx
     CHARACTER(len=:), ALLOCATABLE :: tmp
     err = noerror
@@ -984,5 +1041,5 @@
         err = error("Option "//TRIM(name)//" has no value",-6)
       ELSE
-        IF(.NOT.from_string(tmp,output)) & 
+        IF(.NOT.from_string(tmp,output)) &
         err = error(TRIM(name)//": Cannot convert "//tmp//" to integer.",-10)
       ENDIF
@@ -997,10 +1054,10 @@
     !!  - -6, the option does not have value(s).
     !!  - -10, the value cannot be converted in the output type.
-    OBJECT(cfgparser), INTENT(in) :: this   !! Cfgparser object 
+    OBJECT(cfgparser), INTENT(in) :: this   !! Cfgparser object
     CHARACTER(len=*), INTENT(in)  :: name   !! (Full) Name of the option to get
     LOGICAL, INTENT(out)          :: output !! Output value
-    TYPE(error) :: err 
+    TYPE(error) :: err
       !! Error status
-    INTEGER :: idx 
+    INTEGER :: idx
     CHARACTER(len=:), ALLOCATABLE :: tmp
     err = noerror
@@ -1017,5 +1074,5 @@
         err = error("Option "//TRIM(name)//" has no value",-6)
       ELSE
-        IF(.NOT.from_string(tmp,output)) & 
+        IF(.NOT.from_string(tmp,output)) &
         err = error(TRIM(name)//": Cannot convert "//tmp//" to logical.",-10)
       ENDIF
@@ -1030,10 +1087,10 @@
     !!  - -6, the option does not have value(s).
     !!  - -10, the value cannot be converted in the output type.
-    OBJECT(cfgparser), INTENT(in) :: this   !! Cfgparser object 
+    OBJECT(cfgparser), INTENT(in) :: this   !! Cfgparser object
     CHARACTER(len=*), INTENT(in)  :: name   !! (Full) Name of the option to get
     COMPLEX, INTENT(out)          :: output !! Output value
-    TYPE(error) :: err 
+    TYPE(error) :: err
       !! Error status
-    INTEGER :: idx 
+    INTEGER :: idx
     CHARACTER(len=:), ALLOCATABLE :: tmp
     err = noerror
@@ -1050,5 +1107,5 @@
         err = error("Option "//TRIM(name)//" has no value",-6)
       ELSE
-        IF(.NOT.from_string(tmp,output)) & 
+        IF(.NOT.from_string(tmp,output)) &
         err = error(TRIM(name)//": Cannot convert "//tmp//" to complex.",-10)
       ENDIF
@@ -1062,10 +1119,10 @@
     !!  - -7, no option matches the given name.
     !!  - -6, the option does not have value(s).
-    OBJECT(cfgparser), INTENT(in) :: this   !! Cfgparser object 
+    OBJECT(cfgparser), INTENT(in) :: this   !! Cfgparser object
     CHARACTER(len=*), INTENT(in)  :: name   !! (Full) Name of the option to get
     CHARACTER(len=*), INTENT(out) :: output !! Output value
-    TYPE(error) :: err 
+    TYPE(error) :: err
       !! Error status
-    INTEGER :: idx 
+    INTEGER :: idx
     !CHARACTER(len=:), ALLOCATABLE :: tmp
     err = noerror
@@ -1087,8 +1144,8 @@
     !!
     !! On error, the output vector is not allocated.
-    OBJECT(cfgparser), INTENT(in)                        :: this   !! Cfgparser object 
+    OBJECT(cfgparser), INTENT(in)                        :: this   !! Cfgparser object
     CHARACTER(len=*), INTENT(in)                         :: name   !! (Full) Name of the option to get
     REAL(kind=4), INTENT(out), DIMENSION(:), ALLOCATABLE :: output !! Output values
-    TYPE(error) :: err                                                 
+    TYPE(error) :: err
       !! Error status of the method (see [[cfgparser(type):get_value(bound)]] documentation)
     CHARACTER(len=st_slen), DIMENSION(:), ALLOCATABLE :: tmp
@@ -1114,10 +1171,10 @@
         ELSE IF (.NOT.from_string(tmp(i), output(i))) THEN
           err = error("Cannot convert value #"//TRIM(i2s)//" from option "//TRIM(name),-10)
-          DEALLOCATE(output) ; EXIT  
+          DEALLOCATE(output) ; EXIT
         ENDIF
       ENDDO
     ENDIF
     DEALLOCATE(tmp)
-    RETURN 
+    RETURN
   END FUNCTION cp_get_rv_ve
 
@@ -1126,8 +1183,8 @@
     !!
     !! On error, the output vector is not allocated.
-    OBJECT(cfgparser), INTENT(in)                        :: this   !! Cfgparser object 
+    OBJECT(cfgparser), INTENT(in)                        :: this   !! Cfgparser object
     CHARACTER(len=*), INTENT(in)                         :: name   !! (Full) Name of the option to get
     REAL(kind=8), INTENT(out), DIMENSION(:), ALLOCATABLE :: output !! Output values
-    TYPE(error) :: err                                                 
+    TYPE(error) :: err
       !! Error status of the method (see [[cfgparser(type):get_value(bound)]] documentation)
     CHARACTER(len=st_slen), DIMENSION(:), ALLOCATABLE :: tmp
@@ -1153,10 +1210,10 @@
         ELSE IF (.NOT.from_string(tmp(i), output(i))) THEN
           err = error("Cannot convert value #"//TRIM(i2s)//" from option "//TRIM(name),-10)
-          DEALLOCATE(output) ; EXIT  
+          DEALLOCATE(output) ; EXIT
         ENDIF
       ENDDO
     ENDIF
     DEALLOCATE(tmp)
-    RETURN 
+    RETURN
   END FUNCTION cp_get_dv_ve
 
@@ -1165,8 +1222,8 @@
     !!
     !! On error, the output vector is not allocated.
-    OBJECT(cfgparser), INTENT(in)                   :: this   !! Cfgparser object 
+    OBJECT(cfgparser), INTENT(in)                   :: this   !! Cfgparser object
     CHARACTER(len=*), INTENT(in)                    :: name   !! (Full) Name of the option to get
     INTEGER, INTENT(out), DIMENSION(:), ALLOCATABLE :: output !! Output values
-    TYPE(error) :: err                                                 
+    TYPE(error) :: err
       !! Error status of the method (see [[cfgparser(type):get_value(bound)]] documentation)
     CHARACTER(len=st_slen), DIMENSION(:), ALLOCATABLE :: tmp
@@ -1192,10 +1249,10 @@
         ELSE IF (.NOT.from_string(tmp(i), output(i))) THEN
           err = error("Cannot convert value #"//TRIM(i2s)//" from option "//TRIM(name),-10)
-          DEALLOCATE(output) ; EXIT  
+          DEALLOCATE(output) ; EXIT
         ENDIF
       ENDDO
     ENDIF
     DEALLOCATE(tmp)
-    RETURN 
+    RETURN
   END FUNCTION cp_get_iv_ve
 
@@ -1204,8 +1261,8 @@
     !!
     !! On error, the output vector is not allocated.
-    OBJECT(cfgparser), INTENT(in)                   :: this   !! Cfgparser object 
+    OBJECT(cfgparser), INTENT(in)                   :: this   !! Cfgparser object
     CHARACTER(len=*), INTENT(in)                    :: name   !! (Full) Name of the option to get
     LOGICAL, INTENT(out), DIMENSION(:), ALLOCATABLE :: output !! Output values
-    TYPE(error) :: err                                                 
+    TYPE(error) :: err
       !! Error status of the method (see [[cfgparser(type):get_value(bound)]] documentation)
     CHARACTER(len=st_slen), DIMENSION(:), ALLOCATABLE :: tmp
@@ -1231,10 +1288,10 @@
         ELSE IF (.NOT.from_string(tmp(i), output(i))) THEN
           err = error("Cannot convert value #"//TRIM(i2s)//" from option "//TRIM(name),-10)
-          DEALLOCATE(output) ; EXIT  
+          DEALLOCATE(output) ; EXIT
         ENDIF
       ENDDO
     ENDIF
     DEALLOCATE(tmp)
-    RETURN 
+    RETURN
   END FUNCTION cp_get_lv_ve
 
@@ -1243,8 +1300,8 @@
     !!
     !! On error, the output vector is not allocated.
-    OBJECT(cfgparser), INTENT(in)                   :: this   !! Cfgparser object 
+    OBJECT(cfgparser), INTENT(in)                   :: this   !! Cfgparser object
     CHARACTER(len=*), INTENT(in)                    :: name   !! (Full) Name of the option to get
     COMPLEX, INTENT(out), DIMENSION(:), ALLOCATABLE :: output !! Output values
-    TYPE(error) :: err                                                 
+    TYPE(error) :: err
       !! Error status of the method (see [[cfgparser(type):get_value(bound)]] documentation)
     CHARACTER(len=st_slen), DIMENSION(:), ALLOCATABLE :: tmp
@@ -1270,10 +1327,10 @@
         ELSE IF (.NOT.from_string(tmp(i), output(i))) THEN
           err = error("Cannot convert value #"//TRIM(i2s)//" from option "//TRIM(name),-10)
-          DEALLOCATE(output) ; EXIT  
+          DEALLOCATE(output) ; EXIT
         ENDIF
       ENDDO
     ENDIF
     DEALLOCATE(tmp)
-    RETURN 
+    RETURN
   END FUNCTION cp_get_cv_ve
 
@@ -1282,9 +1339,8 @@
     !!
     !! On error, the output vector is not allocated.
-    OBJECT(cfgparser), INTENT(in)                            :: this   !! Cfgparser object 
+    OBJECT(cfgparser), INTENT(in)                            :: this   !! Cfgparser object
     CHARACTER(len=*), INTENT(in)                             :: name   !! (Full) Name of the option to get
     CHARACTER(len=*), INTENT(out), DIMENSION(:), ALLOCATABLE :: output !! Output values
-    TYPE(error) :: err                                                 
-      !! Error status of the method (see [[cfgparser(type):get_value(bound)]] documentation)
+    TYPE(error) :: err                                                 !! Error status of the method (see [[cfgparser(type):get_value(bound)]] documentation)
     LOGICAL :: ok
     INTEGER :: idx
@@ -1307,15 +1363,15 @@
     !!
     !! If _create_ is given to .true., the method will add a new option if it does not exist in
-    !! the parser. 
-    !! In such case, an error (-6, invalid name) is raised if the option name is not valid.
+    !! the parser.
+    !! In such case, an error ((-9, invalid name)) is raised if the option name is not valid.
     !!
     !! In other case, if the option is not defined in the parser the error status is set to -7.
-    OBJECT(cfgparser), INTENT(inout) :: this   !! Cfgparser object 
+    OBJECT(cfgparser), INTENT(inout) :: this   !! Cfgparser object
     CHARACTER(len=*), INTENT(in)     :: name   !! (Full) Name of the option to set
     REAL(kind=4), INTENT(in)         :: input  !! Input value
-    LOGICAL, INTENT(in), OPTIONAL    :: create !! .true. to create option if it does not exist (default to false). 
+    LOGICAL, INTENT(in), OPTIONAL    :: create !! .true. to create option if it does not exist (default to false).
     TYPE(error)                      :: err    !! Error status
     LOGICAL                       :: zcreate
-    INTEGER                       :: idx 
+    INTEGER                       :: idx
     CHARACTER(len=:), ALLOCATABLE :: sname,pname
     TYPE(words) :: values
@@ -1332,5 +1388,5 @@
       ENDIF
     ELSE
-      this%options(idx)%values = values 
+      this%options(idx)%values = values
     ENDIF
     CALL words_clear(values)
@@ -1341,15 +1397,15 @@
     !!
     !! If _create_ is given to .true., the method will add a new option if it does not exist in
-    !! the parser. 
-    !! In such case, an error (-6, invalid name) is raised if the option name is not valid.
+    !! the parser.
+    !! In such case, an error ((-9, invalid name)) is raised if the option name is not valid.
     !!
     !! In other case, if the option is not defined in the parser the error status is set to -7.
-    OBJECT(cfgparser), INTENT(inout) :: this   !! Cfgparser object 
+    OBJECT(cfgparser), INTENT(inout) :: this   !! Cfgparser object
     CHARACTER(len=*), INTENT(in)     :: name   !! (Full) Name of the option to set
     REAL(kind=8), INTENT(in)         :: input  !! Input value
-    LOGICAL, INTENT(in), OPTIONAL    :: create !! .true. to create option if it does not exist (default to false). 
+    LOGICAL, INTENT(in), OPTIONAL    :: create !! .true. to create option if it does not exist (default to false).
     TYPE(error)                      :: err    !! Error status
     LOGICAL                       :: zcreate
-    INTEGER                       :: idx 
+    INTEGER                       :: idx
     CHARACTER(len=:), ALLOCATABLE :: sname,pname
     TYPE(words) :: values
@@ -1366,5 +1422,5 @@
       ENDIF
     ELSE
-      this%options(idx)%values = values 
+      this%options(idx)%values = values
     ENDIF
     CALL words_clear(values)
@@ -1376,14 +1432,14 @@
     !! If _create_ is given to .true., the method will add a new option if it does not exist in
     !! the parser.
-    !! In such case, an error (-6, invalid name) is raised if the option name is not valid.
+    !! In such case, an error ((-9, invalid name)) is raised if the option name is not valid.
     !!
     !! In other case, if the option is not defined in the parser the error status is set to -7.
-    OBJECT(cfgparser), INTENT(inout) :: this   !! Cfgparser object 
+    OBJECT(cfgparser), INTENT(inout) :: this   !! Cfgparser object
     CHARACTER(len=*), INTENT(in)     :: name   !! (Full) Name of the option to set
     INTEGER, INTENT(in)              :: input  !! Input value
-    LOGICAL, INTENT(in), OPTIONAL    :: create !! .true. to create option if it does not exist (default to false). 
+    LOGICAL, INTENT(in), OPTIONAL    :: create !! .true. to create option if it does not exist (default to false).
     TYPE(error)                      :: err    !! Error status
     LOGICAL                       :: zcreate
-    INTEGER                       :: idx 
+    INTEGER                       :: idx
     CHARACTER(len=:), ALLOCATABLE :: sname,pname
     TYPE(words) :: values
@@ -1395,11 +1451,10 @@
       IF (zcreate) THEN
         err = op_split_name(name,sname,pname)
-        !IF (err == 0) err = cp_add_opt(this,sname,pname,values)
-        err = cp_add_opt(this,sname,pname,values)
+        IF (err == 0) err = cp_add_opt(this,sname,pname,values)
       ELSE
         err = error("Option "//TRIM(name)//" does not exist",-7)
       ENDIF
     ELSE
-      this%options(idx)%values = values 
+      this%options(idx)%values = values
     ENDIF
     CALL words_clear(values)
@@ -1411,14 +1466,14 @@
     !! If _create_ is given to .true., the method will add a new option if it does not exist in
     !! the parser.
-    !! In such case, an error (-6, invalid name) is raised if the option name is not valid.
+    !! In such case, an error ((-9, invalid name)) is raised if the option name is not valid.
     !!
     !! In other case, if the option is not defined in the parser the error status is set to -7.
-    OBJECT(cfgparser), INTENT(inout) :: this   !! Cfgparser object 
+    OBJECT(cfgparser), INTENT(inout) :: this   !! Cfgparser object
     CHARACTER(len=*), INTENT(in)     :: name   !! (Full) Name of the option to set
     LOGICAL, INTENT(in)              :: input  !! Input value
-    LOGICAL, INTENT(in), OPTIONAL    :: create !! .true. to create option if it does not exist (default to false). 
+    LOGICAL, INTENT(in), OPTIONAL    :: create !! .true. to create option if it does not exist (default to false).
     TYPE(error)                      :: err    !! Error status
     LOGICAL                       :: zcreate
-    INTEGER                       :: idx 
+    INTEGER                       :: idx
     CHARACTER(len=:), ALLOCATABLE :: sname,pname
     TYPE(words) :: values
@@ -1435,5 +1490,5 @@
       ENDIF
     ELSE
-      this%options(idx)%values = values 
+      this%options(idx)%values = values
     ENDIF
     CALL words_clear(values)
@@ -1445,14 +1500,14 @@
     !! If _create_ is given to .true., the method will add a new option if it does not exist in
     !! the parser.
-    !! In such case, an error (-6, invalid name) is raised if the option name is not valid.
+    !! In such case, an error ((-9, invalid name)) is raised if the option name is not valid.
     !!
     !! In other case, if the option is not defined in the parser the error status is set to -7.
-    OBJECT(cfgparser), INTENT(inout) :: this   !! Cfgparser object 
+    OBJECT(cfgparser), INTENT(inout) :: this   !! Cfgparser object
     CHARACTER(len=*), INTENT(in)     :: name   !! (Full) Name of the option to set
     COMPLEX, INTENT(in)              :: input  !! Input value
-    LOGICAL, INTENT(in), OPTIONAL    :: create !! .true. to create option if it does not exist (default to false). 
+    LOGICAL, INTENT(in), OPTIONAL    :: create !! .true. to create option if it does not exist (default to false).
     TYPE(error)                      :: err    !! Error status
     LOGICAL                       :: zcreate
-    INTEGER                       :: idx 
+    INTEGER                       :: idx
     CHARACTER(len=:), ALLOCATABLE :: sname,pname
     TYPE(words) :: values
@@ -1469,5 +1524,5 @@
       ENDIF
     ELSE
-      this%options(idx)%values = values 
+      this%options(idx)%values = values
     ENDIF
     CALL words_clear(values)
@@ -1479,14 +1534,14 @@
     !! If _create_ is given to .true., the method will add a new option if it does not exist in
     !! the parser.
-    !! In such case, an error (-6, invalid name) is raised if the option name is not valid.
+    !! In such case, an error ((-9, invalid name)) is raised if the option name is not valid.
     !!
     !! In other case, if the option is not defined in the parser the error status is set to -7.
-    OBJECT(cfgparser), INTENT(inout) :: this   !! Cfgparser object 
+    OBJECT(cfgparser), INTENT(inout) :: this   !! Cfgparser object
     CHARACTER(len=*), INTENT(in)     :: name   !! (Full) Name of the option to set
     CHARACTER(len=*), INTENT(in)     :: input  !! Input value
-    LOGICAL, INTENT(in), OPTIONAL    :: create !! .true. to create option if it does not exist (default to false). 
+    LOGICAL, INTENT(in), OPTIONAL    :: create !! .true. to create option if it does not exist (default to false).
     TYPE(error)                      :: err    !! Error status
     LOGICAL                       :: zcreate
-    INTEGER                       :: idx 
+    INTEGER                       :: idx
     CHARACTER(len=:), ALLOCATABLE :: sname,pname
     TYPE(words) :: values
@@ -1503,5 +1558,5 @@
       ENDIF
     ELSE
-      this%options(idx)%values = values 
+      this%options(idx)%values = values
     ENDIF
     CALL words_clear(values)
@@ -1512,9 +1567,9 @@
     !!
     !! If _create_ is given to .true., the method will add a new option if it does not exist in
-    !! the parser. 
-    !! In such case, an error (-6, invalid name) is raised if the option name is not valid.
+    !! the parser.
+    !! In such case, an error ((-9, invalid name)) is raised if the option name is not valid.
     !!
     !! In other case, if the option is not defined in the parser the error status is set to -7.
-    OBJECT(cfgparser), INTENT(inout)       :: this   !! Cfgparser object 
+    OBJECT(cfgparser), INTENT(inout)       :: this   !! Cfgparser object
     CHARACTER(len=*), INTENT(in)           :: name   !! (Full) Name of the option to get
     REAL(kind=4), INTENT(in), DIMENSION(:) :: input  !! Input values
@@ -1537,5 +1592,5 @@
       ENDIF
     ELSE
-      this%options(idx)%values = values 
+      this%options(idx)%values = values
     ENDIF
     CALL words_clear(values)
@@ -1547,8 +1602,8 @@
     !! If _create_ is given to .true., the method will add a new option if it does not exist in
     !! the parser.
-    !! In such case, an error (-6, invalid name) is raised if the option name is not valid.
+    !! In such case, an error ((-9, invalid name)) is raised if the option name is not valid.
     !!
     !! In other case, if the option is not defined in the parser the error status is set to -7.
-    OBJECT(cfgparser), INTENT(inout)       :: this   !! Cfgparser object 
+    OBJECT(cfgparser), INTENT(inout)       :: this   !! Cfgparser object
     CHARACTER(len=*), INTENT(in)           :: name   !! (Full) Name of the option to get
     REAL(kind=8), INTENT(in), DIMENSION(:) :: input  !! Input values
@@ -1571,5 +1626,5 @@
       ENDIF
     ELSE
-      this%options(idx)%values = values 
+      this%options(idx)%values = values
     ENDIF
     CALL words_clear(values)
@@ -1580,9 +1635,9 @@
     !!
     !! If _create_ is given to .true., the method will add a new option if it does not exist in
-    !! the parser. 
-    !! In such case, an error (-6, invalid name) is raised if the option name is not valid.
+    !! the parser.
+    !! In such case, an error ((-9, invalid name)) is raised if the option name is not valid.
     !!
     !! In other case, if the option is not defined in the parser the error status is set to -7.
-    OBJECT(cfgparser), INTENT(inout)  :: this   !! Cfgparser object 
+    OBJECT(cfgparser), INTENT(inout)  :: this   !! Cfgparser object
     CHARACTER(len=*), INTENT(in)      :: name   !! (Full) Name of the option to get
     INTEGER, INTENT(in), DIMENSION(:) :: input  !! Input values
@@ -1605,5 +1660,5 @@
       ENDIF
     ELSE
-      this%options(idx)%values = values 
+      this%options(idx)%values = values
     ENDIF
     CALL words_clear(values)
@@ -1615,8 +1670,8 @@
     !! If _create_ is given to .true., the method will add a new option if it does not exist in
     !! the parser.
-    !! In such case, an error (-6, invalid name) is raised if the option name is not valid.
+    !! In such case, an error ((-9, invalid name)) is raised if the option name is not valid.
     !!
     !! In other case, if the option is not defined in the parser the error status is set to -7.
-    OBJECT(cfgparser), INTENT(inout)  :: this   !! Cfgparser object 
+    OBJECT(cfgparser), INTENT(inout)  :: this   !! Cfgparser object
     CHARACTER(len=*), INTENT(in)      :: name   !! (Full) Name of the option to get
     LOGICAL, INTENT(in), DIMENSION(:) :: input  !! Input values
@@ -1639,5 +1694,5 @@
       ENDIF
     ELSE
-      this%options(idx)%values = values 
+      this%options(idx)%values = values
     ENDIF
     CALL words_clear(values)
@@ -1648,9 +1703,9 @@
     !!
     !! If _create_ is given to .true., the method will add a new option if it does not exist in
-    !! the parser. 
-    !! In such case, an error (-6, invalid name) is raised if the option name is not valid.
+    !! the parser.
+    !! In such case, an error ((-9, invalid name)) is raised if the option name is not valid.
     !!
     !! In other case, if the option is not defined in the parser the error status is set to -7.
-    OBJECT(cfgparser), INTENT(inout)  :: this   !! Cfgparser object 
+    OBJECT(cfgparser), INTENT(inout)  :: this   !! Cfgparser object
     CHARACTER(len=*), INTENT(in)      :: name   !! (Full) Name of the option to get
     COMPLEX, INTENT(in), DIMENSION(:) :: input  !! Input values
@@ -1673,5 +1728,5 @@
       ENDIF
     ELSE
-      this%options(idx)%values = values 
+      this%options(idx)%values = values
     ENDIF
     CALL words_clear(values)
@@ -1683,8 +1738,8 @@
     !! If _create_ is given to .true., the method will add a new option if it does not exist in
     !! the parser.
-    !! In such case, an error (-6, invalid name) is raised if the option name is not valid.
+    !! In such case, an error ((-9, invalid name)) is raised if the option name is not valid.
     !!
     !! In other case, if the option is not defined in the parser the error status is set to -7.
-    OBJECT(cfgparser), INTENT(inout)           :: this   !! Cfgparser object 
+    OBJECT(cfgparser), INTENT(inout)           :: this   !! Cfgparser object
     CHARACTER(len=*), INTENT(in)               :: name   !! (Full) Name of the option to get
     CHARACTER(len=*), INTENT(in), DIMENSION(:) :: input  !! Input values
@@ -1707,5 +1762,5 @@
       ENDIF
     ELSE
-      this%options(idx)%values = values 
+      this%options(idx)%values = values
     ENDIF
     CALL words_clear(values)
@@ -1734,5 +1789,5 @@
     LOGICAL                       :: zoverride,ok,has_opt
     INTEGER                       :: lineno,lu,i
-    CHARACTER(len=2), PARAMETER   :: space = CHAR(32)//","    ! check if , is really wanted..
+    CHARACTER(len=2), PARAMETER   :: space = CHAR(32)//","    ! check if , is really wanted... A: YES space are the delimiter of the words internal object !
     CHARACTER(len=2), PARAMETER   :: blanks = CHAR(9)//CHAR(32) ! currently not used because blanks truncate.
     CHARACTER(len=15)             :: sln
@@ -1746,9 +1801,9 @@
     zoverride = .false. ; IF (PRESENT(override)) zoverride = override
     ! initialize local variables
-    curval = '' ; line   = '' ; name  = '' ; value = '' 
-    lineno = 0  ; lu = free_lun() 
+    curval = '' ; line   = '' ; name  = '' ; value = ''
+    lineno = 0  ; lu = free_lun()
     IF (LEN_TRIM(isec) == 0) isec = "__default__"
     i = INDEX(TRIM(path),"/",.true.)
-    IF (i == 0) THEN 
+    IF (i == 0) THEN
       fulp = fs_realpath("./"//TRIM(ADJUSTL(path)))
     ELSE
@@ -1763,5 +1818,5 @@
       RETURN
     ENDIF
-    ! check for lun 
+    ! check for lun
     IF (lu == -1) THEN ; err = error("No available logical unit",-12) ; RETURN ; ENDIF
     OPEN(lu,FILE=TRIM(path),STATUS='old',ACTION='READ')
@@ -1775,5 +1830,5 @@
           ! 1) get relative path
           ipath = fs_relpath(ipath,dirp)
-          ! 2) compute asbolute path)
+          ! 2) compute asbolute path
           ipath = TRIM(dirp)//"/"//TRIM(ipath)
           ipath = fs_realpath(ipath)
@@ -1793,5 +1848,5 @@
             CALL op_clear(curopt); curval = ''
           ENDIF
-          err = read_include(parser,ipath,isec,ipaths,zoverride) 
+          err = read_include(parser,ipath,isec,ipaths,zoverride)
           IF (err /= 0) EXIT
         ENDIF
@@ -1799,5 +1854,5 @@
       ENDIF
       ! continuation line ?
-      IF (SCAN(line(1:1),blanks) /= 0 .AND. op_valid(curopt)) THEN 
+      IF (SCAN(line(1:1),blanks) /= 0 .AND. op_valid(curopt)) THEN
           IF (LEN(curval) == 0) THEN
             curval = strip_comment(line)
@@ -1806,5 +1861,5 @@
           ENDIF
       ELSE
-       ! 1. Remove comment part and left adjust line 
+       ! 1. Remove comment part and left adjust line
        line = strip_comment(line)
        ! a section header or option header?
@@ -1837,5 +1892,5 @@
            ! 3. curval is set to value
            ! 4. update curval
-           IF (op_valid(curopt)) THEN 
+           IF (op_valid(curopt)) THEN
               IF (LEN(curval) > 0) &
               CALL words_extend(curopt%values,TRIM(ADJUSTL(curval)),space,.true.,.true.)
@@ -1847,8 +1902,8 @@
            ENDIF
            CALL op_clear(curopt) ; curval = ''
-           has_opt = cfg_has_option(parser,TRIM(isec)//"/"//TRIM(name)) 
+           has_opt = cfg_has_option(parser,TRIM(isec)//"/"//TRIM(name))
 
            IF (has_opt.AND..NOT.zoverride) THEN
-             ! it is an error: no duplicate allowed 
+             ! it is an error: no duplicate allowed
              err = error(basp//'(L'//TRIM(sln)//"): Duplicate option '"//TRIM(name)//"' in "//isec,-8)
              EXIT
@@ -1859,10 +1914,10 @@
            curval = value
          CASE(cfg_UNKNOWN)
-           ! unknown handles also invalid name: it is a critical error 
+           ! unknown handles also invalid name: it is a critical error
            IF (err == -9) EXIT
-       END SELECT 
+       END SELECT
       ENDIF
     ENDDO
-    IF (op_valid(curopt)) THEN 
+    IF (op_valid(curopt)) THEN
       IF (LEN(curval) > 0) &
       CALL words_extend(curopt%values,TRIM(ADJUSTL(curval)),space,.true.,.true.)
@@ -1886,6 +1941,6 @@
       !!   it is a section header.
       !! - Otherwise, if line has '=', without '#' before '=', it is an option.
-      !! 
-      !! Then the method returns an integer with the kind flag of the statement which is one of 
+      !!
+      !! Then the method returns an integer with the kind flag of the statement which is one of
       !! -1 (cfg_UNKNOWN), 0 (cfg_SECTION) or 1 (cfg_OPTION).
       CHARACTER(len=*), INTENT(in)               :: string  !! Input string to process
@@ -1898,15 +1953,16 @@
       kind = cfg_UNKNOWN
       ! get a trimmed (and left adjusted) copy
-      copy = TRIM(string) 
+      copy = TRIM(string)
       ! Is it a section ?
       !   ---> search for subscripts of '[' and ']'
       !   ---> check that '[' is 1st char and ']' is last char
-      bi = INDEX(copy,'[') ; ei = INDEX(copy,']') 
+      bi = INDEX(copy,'[') ; ei = INDEX(copy,']')
       IF (bi == 1 .AND. ei == LEN(copy) .AND. bi < ei) THEN
         ! it is a section header
         kind = cfg_SECTION
         ! get section name: adjust and trim to remove extra blank spaces
-        name = str_to_lower(TRIM(ADJUSTL(copy(bi+1:ei-1))))
-        IF (TRIM(name) /= "__default__" .AND. .NOT.cfg_check_name(name)) THEN 
+        name = TRIM(ADJUSTL(copy(bi+1:ei-1)))
+        ! hack cfg_check_name: append '/a' to get a valid option part to test
+        IF (TRIM(name) /= "__default__" .AND. .NOT.cfg_check_name(name//"/a")) THEN
           kind = cfg_UNKNOWN
           err = error("Invalid section name ("//name//")",-9)
@@ -1916,13 +1972,13 @@
       ELSE
         ! Is it an option ?
-        !   --> search for '=' and check if it is set before 
+        !   --> search for '=' and check if it is set before
         !       1st quote (if any)
-        bi = INDEX(copy,"=") 
+        bi = INDEX(copy,"=")
         ! search for quotes
         ei = SCAN(copy,quotes) ; IF (ei==0) ei = LEN(copy)+1
         IF (bi /= 0 .AND. bi < ei) THEN
           kind = cfg_OPTION
-          name = str_to_lower(TRIM(copy(1:bi-1)))
-          IF (.NOT.cfg_check_name(name)) THEN 
+          name = to_lower(TRIM(copy(1:bi-1)))
+          IF (.NOT.cfg_check_name(name)) THEN
             kind = cfg_UNKNOWN
             err = error("Invalid option name ("//TRIM(name)//")",-9)
@@ -1944,10 +2000,10 @@
     FUNCTION strip_comment(line) RESULT(stripped)
       !! Replace comments part of a string by blank spaces
-      !! The method replaces every characters after '#' (included) by spaces. 
-      !! @note 
+      !! The method replaces every characters after '#' (included) by spaces.
+      !! @note
       !! Output string is also left adjusted, thus only trailing blank can be present.
       CHARACTER(len=*), INTENT(in) :: line !! A string to process
       CHARACTER(len=LEN(line)) :: stripped !! A string of same length than 'line' but without comment(s)
-      
+
       INTEGER :: idx
       stripped = ADJUSTL(line)
@@ -1960,10 +2016,10 @@
       !! Read a complete line
       !!
-      !! Each time it is CALLed, the function reads a complete of the file opened in 'lun' logical 
+      !! Each time it is CALLed, the function reads a complete of the file opened in 'lun' logical
       !! unit and returns .false. if EOF has been reached, .true. otherwise.
       !!
       !! The function is intended to read a file line by line:
-      !! 
-      !! ```fortran
+      !!
+      !! ```
       !! lu = 1
       !! open(lu,file="path/to/the/file/to/read")
@@ -1975,10 +2031,10 @@
       !! CLOSE(1)
       !! ```
-      INTEGER, INTENT(in)                        :: lun     !! Logical unit with the opened file to read. 
-      CHARACTER(len=:), INTENT(out), ALLOCATABLE :: string  !! Output processed line 
+      INTEGER, INTENT(in)                        :: lun     !! Logical unit with the opened file to read.
+      CHARACTER(len=:), INTENT(out), ALLOCATABLE :: string  !! Output processed line
       LOGICAL                                    :: not_eof !! .true. if EOF has NOT been reached yet, .false. otherwise
       CHARACTER(len=50) :: buf
       INTEGER           :: e,sz
-      not_eof = .true. ; string = '' 
+      not_eof = .true. ; string = ''
       DO
         READ(lun,'(a)',ADVANCE="no",SIZE=sz,IOSTAT=e) buf
@@ -2006,5 +2062,5 @@
       CHARACTER(len=:), INTENT(out), ALLOCATABLE :: incpath
         !! A string with the filepath to be included if '#include' statement is found, empty string otherwise
-      LOGICAL :: res 
+      LOGICAL :: res
         !! .true. if line is a comment or an empty string, .false. otherwise
       CHARACTER(len=:), ALLOCATABLE :: copy
@@ -2017,5 +2073,5 @@
         ! search for include statement
         ! IMPORTANT: assume that there is only a path after include statement
-        IF (INDEX(copy,"#include ") == 1) incpath = TRIM(ADJUSTL(copy(10:)))
+        IF (INDEX(copy,"#include ") == 1) incpath = remove_quotes(TRIM(ADJUSTL(copy(10:))))
       ENDIF
       RETURN
@@ -2046,5 +2102,5 @@
   SUBROUTINE insertionSort(opts)
     !! Sort an array of Options using insertion sort algorithm
-    TYPE(option), INTENT(inout), DIMENSION(:) :: opts !! Array to sort. 
+    TYPE(option), INTENT(inout), DIMENSION(:) :: opts !! Array to sort.
     TYPE(option) :: temp
     INTEGER :: i, j
@@ -2058,5 +2114,5 @@
         ELSE
           EXIT
-        ENDIF 
+        ENDIF
       ENDDO
       opts(j+1) = temp
@@ -2065,28 +2121,4 @@
   END SUBROUTINE insertionSort
 
-  FUNCTION free_lun() RESULT(lu)
-    !> Get the first free logical unit
-    !!
-    !! The function loops from 7 to 9999 and returns the first free logical unit.
-    !! @note
-    !! According to Fortran standard, the maximum value for a lun is processor
-    !! dependent. I just assume that [7,9999] is a valid range and I believe that 
-    !! 9992 files to be opened is far enough for any program !
-    !! @note
-    !! If you intend to use loggers object from this library, you should keep in
-    !! mind that loggers open files with the first free logical unit. Consequently
-    !! if you need to perform I/O operations you should use this function to get a
-    !! free lun instead of just randomly set a lun ! 
-    INTEGER :: lu
-      !! First free logical unit in the range [7,999]  or -1 if no lun is available
-    INTEGER, PARAMETER :: mxlu = 9999
-    LOGICAL :: notfree
-    lu = 6 ; notfree = .true.
-    DO WHILE(notfree.AND.lu<=mxlu)
-      lu=lu+1 ; INQUIRE(unit=lu,OPENED=notfree)
-    ENDDO
-    IF (lu >= mxlu) lu = -1
-  END FUNCTION free_lun
-
 END MODULE CFGPARSE
 
Index: trunk/LMDZ.TITAN/libf/muphytitan/config/config.ne15.cfg
===================================================================
--- trunk/LMDZ.TITAN/libf/muphytitan/config/config.ne15.cfg	(revision 1897)
+++ trunk/LMDZ.TITAN/libf/muphytitan/config/config.ne15.cfg	(revision 1897)
@@ -0,0 +1,99 @@
+# YAMMS model parameters
+# ======================
+
+### Model global parameters 
+###------------------------
+# Monomer radius (m)
+rm                     = 50e-9
+# Fractal dimension
+df                     = 2.0
+# Aerosol density (kg.m-3)
+rho_aer                = 1000.
+# Enable/disable Haze production process 
+haze_production        = T
+# Enable/disable Haze coagulation process 
+haze_coagulation       = T
+# Coagulation interactions, a combination of:
+#    0 - no interactions (same as haze_coagulation == F)
+#    1 - SS interactions
+#    2 - SF interactions
+#    4 - FF interactions.
+# (for example: 5 = 4+1 --> SS and FF coagulation only)
+haze_coag_interactions = 7
+# Enable/disable Haze sedimentation process 
+haze_sedimentation     = T
+# Disable Fiadero correction for sedimentation process
+no_fiadero             = F
+# Fiadero correction minimum ratio threshold
+fiadero_min_ratio      = 0.1
+# Fiadero correction maximum ratio threshold
+fiadero_max_ratio      = 10.
+# Force settling velocity to M0
+wsed_m0 = T
+# Force settling velocity to M3
+wsed_m3 = F
+# Enable/disable clouds sedimentation process
+# (automatically set to F if clouds microphysics is not enabled)
+clouds_sedimentation   = T
+# Enable/disable clouds nucleation and condensation processes
+# (automatically set to F if clouds microphysics is not enabled)
+clouds_nuc_cond        = T
+# Condensible species configuration file
+# (not needed if clouds microphysics is not enabled)
+specie_cfg             = ../datagcm/microphysics/mp2m_species.cfg
+
+# Enable/disable spherical mode transfert probability
+transfert_probability = T
+# Path of the spherical mode transfert probability look-up tables file
+# (optional if 'transfert_probability' is False) 
+ps2s_file             = ../datagcm/microphysics/mmp_ps2s_rm50_ne15.nc
+
+# Electric charging coagulation correction
+# If set to .false. then no correction is assumed
+electric_charging     = T 
+# Path of the electric charging correction factor.
+# (optional if 'electric_charging' is False) 
+mq_file               = ../datagcm/microphysics/mmp_qmean_rm50_ne15.nc
+
+#  alpha_X sections contain the parameters of the inter-moments relation function for
+#  the mode X: either spherical (s) or fractal (f)
+#  dndr_X sections contain the parameters of the size-distribution law of the mode X
+[alpha_s]
+a =   1.5044478E-02,  -2.0948806E-01,  -1.5824302E+02,   1.1597818E-01,   9.9502283E-02,  -1.1223796E-01
+b =  -2.8622255E-01,   7.7089599E+00,  -1.7000626E+02,   2.6012143E+00,   5.5138784E-01,   9.2024747E-01
+c =  -3.0205020E-02,  -3.5510239E+01,  -2.0306468E+02,  -1.3605159E+01,  -4.1653422E+00,  -4.2571698E+00
+[dndr_s]
+rc = 4.58219580180634588E-007
+a0 = 86144.861255561875
+c  = 0d0
+a  = 2.48333861883769357E-040, 1.46076790655632173E-013, 1.71525517568997062E-009,
+    1.80855172875974993E-019, 1.48212594918347503E-047, 6.87247318898338451E-081
+b  = 59.518212357684796, 15.507500262021228, -5.4179933012448069,
+    -9.3500794017892854, -18.207927270524777, -27.248924688740562
+[alpha_f]
+a =   1.5044478E-02,  -2.0948806E-01,  -1.5824302E+02,   1.1597818E-01,   9.9502283E-02,  -1.1223796E-01
+b =  -2.8622255E-01,   7.7089599E+00,  -1.7000626E+02,   2.6012143E+00,   5.5138784E-01,   9.2024747E-01
+c =  -3.0205020E-02,  -3.5510239E+01,  -2.0306468E+02,  -1.3605159E+01,  -4.1653422E+00,  -4.2571698E+00
+[dndr_f]
+rc = 4.58219580180634588E-007
+a0 = 86144.861255561875
+c  = 0d0
+a  = 2.48333861883769357E-040, 1.46076790655632173E-013, 1.71525517568997062E-009,
+    1.80855172875974993E-019, 1.48212594918347503E-047, 6.87247318898338451E-081
+b  = 59.518212357684796, 15.507500262021228, -5.4179933012448069,
+    -9.3500794017892854, -18.207927270524777, -27.248924688740562
+
+# ================= #
+# b^T_k cofficients #
+# ================= #
+# This section gathers the values of all the btk coefficient used in the coagulation
+# equations for the free-molecular regime.
+[btks]
+bt0 = 0.73d0, 0.73d0, 0.75d0, 0.99d0, 0.00d0
+bt3 = 0.97d0, 0.97d0, 0.00d0, 0.99d0, 0.99d0
+
+[optics]
+optic_file = /path/to/optics_look_up_table.nc
+
+
+
Index: trunk/LMDZ.TITAN/libf/muphytitan/csystem.c
===================================================================
--- trunk/LMDZ.TITAN/libf/muphytitan/csystem.c	(revision 1896)
+++ trunk/LMDZ.TITAN/libf/muphytitan/csystem.c	(revision 1897)
@@ -1,5 +1,5 @@
-/* Copyright Jérémie Burgalat (2010-2015)
- * 
- * burgalat.jeremie@gmail.com
+/* Copyright Jérémie Burgalat (2010-2015,2017)
+ * 
+ * jeremie.burgalat@univ-reims.fr
  * 
  * This software is a computer program whose purpose is to provide configuration 
@@ -38,4 +38,5 @@
 #include <stdio.h>
 #include <limits.h>
+#include <string.h>
 #include <libgen.h>
 #include <sys/param.h>  // MAXPATHLEN
@@ -49,5 +50,4 @@
 #include <time.h>
 #include <unistd.h>
-
 #include "csystem.h"
 
@@ -270,4 +270,56 @@
 }
 
+
+int c_copy(const char *to, const char *from) {
+  int fd_to, fd_from;
+  char buf[4096];
+  ssize_t nread;
+  int saved_errno;
+
+  fd_from = open(from, O_RDONLY);
+  if (fd_from < 0)
+    return -1;
+
+  fd_to = open(to, O_WRONLY | O_CREAT | O_EXCL, 0666);
+  if (fd_to < 0)
+    goto out_error;
+
+  while (nread = read(fd_from, buf, sizeof buf), nread > 0) {
+    char *out_ptr = buf;
+    ssize_t nwritten;
+
+    do {
+      nwritten = write(fd_to, out_ptr, nread);
+
+      if (nwritten >= 0) {
+        nread -= nwritten;
+        out_ptr += nwritten;
+      } else if (errno != EINTR) {
+        goto out_error;
+      }
+    } while (nread > 0);
+  }
+
+  if (nread == 0) {
+    if (close(fd_to) < 0) {
+      fd_to = -1;
+      goto out_error;
+    }
+    close(fd_from);
+    /* Success! */
+    return 0;
+  }
+
+out_error:
+  saved_errno = errno;
+
+  close(fd_from);
+  if (fd_to >= 0)
+    close(fd_to);
+
+  errno = saved_errno;
+  return 1;
+}
+
 /* Remove file from filesytem */
 int c_remove(const char *path){
@@ -490,66 +542,149 @@
 }
 
-/* This verison of realpath was a dumb attempt to get a resolved path
- * that may not exist. As a matter of fact, it cannot cover all range 
- * of possibilities.
- * Thus it has been removed. The current verison of c_realpath simply
- * uses POSIX realpath and returns CWD if no path is given...
+/*
+ * Author:  David Robert Nadeau
+ * Site:    http://NadeauSoftware.com/
+ * License: Creative Commons Attribution 3.0 Unported License
+ *          http://creativecommons.org/licenses/by/3.0/deed.en_US
+ *
+ * Cross-plateform way to get memory usage at a given time in fucking bytes !!
+ *
+ * Usage:
+ *
+ * size_t currentSize = getCurrentRSS( );
+ * size_t peakSize    = getPeakRSS( );
  */
 
-/* Get the realpath of input path and saves it in output path */ 
-/*
-char* c_realpath(const char *input){
-  char *in,*output, *cur,*tmp,*res,*cwd ;
-  int c,om,rm,im; 
-  output = NULL;
-  cwd = c_getcwd();
-  if(!strlen(input))
-    return cwd;
-  // check (ugly) if path is absolute
-  if (input[0] == '/') {
-    in = strdup(input);
-  }else{
-    fprintf(stderr,"+++ C( in): %s\n",input);
-    in = malloc((strlen(input)+strlen(cwd)+2)*sizeof(char));
-    strncpy(in, cwd, strlen(cwd)); 
-    strncpy(&in[strlen(cwd)+1], input, strlen(input)); 
-    in[strlen(cwd)] = '/';
-    in[strlen(input)+strlen(cwd)+1] = '\0';
-  }
-  fprintf(stderr,"C( in): %s\n",in);
-  c = om = im = strlen(in);
-  tmp = malloc((c+1)*sizeof(char)); 
-  tmp[c] ='\0';
-  // search for the deepest existing directory in "input" path
-  while(c != 0 && (res = realpath(tmp,NULL)) == NULL){
-    // search for the next / from the right (i.e. parent directory)
-    for (cur=tmp+strlen(tmp) ; *cur != '/' && cur != tmp ; cur--, c--);
-    free(tmp) ; tmp = malloc((c+1)*sizeof(char)); 
-    strncpy(tmp, in, c); tmp[c] ='\0';
-  }
-  free(tmp);
-  // On error: null string
-  if (c == 0 || (res==NULL)) {
-    output = (char *)malloc(sizeof(char));
-    output[0]='\0'; 
-    return output; 
-  }
-  // no error: allocate output string
-  rm = strlen(res);
-  om += rm-c;
-  output = (char *)malloc(om*sizeof(char));
-  strncpy(output,res,rm);
-  // Here we could have a bug:
-  //    If the path does not exist and contains .. or . references after the deepest
-  //    existing directory, it will simply be appended !
-  //    Thus we can never resolve the path.
-  if (c != im)
-    strncpy(output+rm,in+c,im-c);
-    //strncpy(output+rm,in+c,im-c-1);
-  output[om] = '\0';
-  fprintf(stderr,"C(tmp): %s\n",res);
-  fprintf(stderr,"C(out): %s\n",output);
-  free(res) ;
-  return output;
-}
-*/
+#if defined(_WIN32)
+#include <windows.h>
+#include <psapi.h>
+#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
+#include <sys/resource.h>
+#if defined(__APPLE__) && defined(__MACH__)
+#include <mach/mach.h>
+#elif(defined(_AIX) || defined(__TOS__AIX__)) ||                                                                               \
+    (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__)))
+#include <procfs.h>
+#endif
+#endif
+
+/**
+ * Returns the peak (maximum so far) resident set size (physical
+ * memory use) measured in bytes, or zero if the value cannot be
+ * determined on this OS.
+ */
+size_t c_getPeakRSS() {
+#if defined(_WIN32)
+    /* Windows -------------------------------------------------- */
+    PROCESS_MEMORY_COUNTERS info;
+    GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
+    return (size_t)info.PeakWorkingSetSize;
+
+#elif(defined(_AIX) || defined(__TOS__AIX__)) ||                                                                               \
+    (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__)))
+    /* AIX and Solaris ------------------------------------------ */
+    struct psinfo psinfo;
+    int fd = -1;
+    if ((fd = open("/proc/self/psinfo", O_RDONLY)) == -1)
+        return (size_t)0L; /* Can't open? */
+    if (read(fd, &psinfo, sizeof(psinfo)) != sizeof(psinfo)) {
+        close(fd);
+        return (size_t)0L; /* Can't read? */
+    }
+    close(fd);
+    return (size_t)(psinfo.pr_rssize * 1024L);
+
+#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
+    /* BSD, Linux, and OSX -------------------------------------- */
+    struct rusage rusage;
+    getrusage(RUSAGE_SELF, &rusage);
+#if defined(__APPLE__) && defined(__MACH__)
+    return (size_t)rusage.ru_maxrss;
+#else
+    return (size_t)(rusage.ru_maxrss * 1024L);
+#endif
+
+#else
+    /* Unknown OS ----------------------------------------------- */
+    return (size_t)0L; /* Unsupported. */
+#endif
+}
+
+/**
+ * Returns the current resident set size (physical memory use) measured
+ * in bytes, or zero if the value cannot be determined on this OS.
+ */
+size_t c_getCurrentRSS() {
+#if defined(_WIN32)
+    /* Windows -------------------------------------------------- */
+    PROCESS_MEMORY_COUNTERS info;
+    GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
+    return (size_t)info.WorkingSetSize;
+
+#elif defined(__APPLE__) && defined(__MACH__)
+    /* OSX ------------------------------------------------------ */
+    struct mach_task_basic_info info;
+    mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT;
+    if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &infoCount) != KERN_SUCCESS)
+        return (size_t)0L; /* Can't access? */
+    return (size_t)info.resident_size;
+
+#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__)
+    /* Linux ---------------------------------------------------- */
+    long rss = 0L;
+    FILE *fp = NULL;
+    if ((fp = fopen("/proc/self/statm", "r")) == NULL)
+        return (size_t)0L; /* Can't open? */
+    if (fscanf(fp, "%*s%ld", &rss) != 1) {
+        fclose(fp);
+        return (size_t)0L; /* Can't read? */
+    }
+    fclose(fp);
+    return (size_t)rss * (size_t)sysconf(_SC_PAGESIZE);
+
+#else
+    /* AIX, BSD, Solaris, and Unknown OS ------------------------ */
+    return (size_t)0L; /* Unsupported. */
+#endif
+}
+
+
+ 
+/* Get some informatiosn about the OS memory usage (from /proc/meminfo) */
+int c_getSystemMemory(long long int *m_total,long long int *m_available,long long int *m_free){
+    FILE* fp;
+    char buf[1024];
+    char *tmp,*p;
+    int ts;
+    long long int mem1 = 0L,mem2 = 0L,mem3 = 0L;
+    if (m_total) (*m_total) = mem1;
+    if (m_available) (*m_free) = mem2;
+    if (m_free) (*m_free) = mem3;
+    if ((fp = fopen("/proc/meminfo", "r")) == NULL)  
+        return 1;
+    while (fgets(buf, sizeof(buf), fp) != NULL){
+        ts = strlen(buf) - 1; buf[ts] = '\0'; 
+        tmp = strndup(&buf[0],ts);
+        // check our 3 cases
+        if (ts >= 9 && !strncmp(tmp,"MemTotal:",9)){
+            // extract the two first tokens.
+            p=strtok(tmp, " "); p=strtok(NULL, " ");
+            // convert the value.
+            mem1 = strtoll(p,NULL,10);
+        }
+        if (ts >= 13 && !strncmp(tmp,"MemAvailable:",13)){
+            p=strtok(tmp, " "); p=strtok(NULL, " "); 
+            mem2 = strtoll(p,NULL,10);
+        }    
+        if (ts >= 8 && !strncmp(tmp,"MemFree:",8)){
+            p=strtok(tmp, " "); p=strtok(NULL, " "); 
+            mem3 = strtoll(p,NULL,10);
+        }
+        free(tmp);
+    }
+    fclose(fp);
+    if (m_total) (*m_total) = mem1;
+    if (m_available) (*m_available) = mem2;
+    if (m_free) (*m_free) = mem3;
+    return 0;
+}
Index: trunk/LMDZ.TITAN/libf/muphytitan/csystem.h
===================================================================
--- trunk/LMDZ.TITAN/libf/muphytitan/csystem.h	(revision 1896)
+++ trunk/LMDZ.TITAN/libf/muphytitan/csystem.h	(revision 1897)
@@ -1,5 +1,5 @@
-/* Copyright Jérémie Burgalat (2010-2015)
- * 
- * burgalat.jeremie@gmail.com
+/* Copyright Jérémie Burgalat (2010-2015,2017)
+ * 
+ * jeremie.burgalat@univ-reims.fr
  * 
  * This software is a computer program whose purpose is to provide configuration 
@@ -31,5 +31,5 @@
  * The fact that you are presently reading this means that you have had
  * knowledge of the CeCILL-B license and that you accept its terms.
- *//* csystem.h */
+ */
 
 
@@ -172,4 +172,13 @@
  */ 
 int c_mkdir(const char *path, mode_t mode);
+
+
+/**
+ * Copy file to another.
+ * @param to A C string with the new filepath
+ * @param from A C string with the filepath to copy
+ * @return An integer with 0 on success, 1 on failure.
+ */
+int c_copy(const char *to, const char *from);
 
 /**
@@ -255,2 +264,19 @@
 int c_termsize(int *rows,int *cols);
 
+/**
+ * Get the current resident set size memory used by the program.
+ */
+size_t c_getCurrentRSS();
+
+/**
+ * Get the peak resident set size memory used by the program.
+ */
+size_t c_getPeakRSS();
+
+/**
+ * Get global memory usage informations.
+ *
+ * Note: The method attempts to read /proc/meminfo. If the file does not exists, all the given output arguments are
+ * set to zero.
+ */
+int c_getSystemMemory(long long int *m_total,long long int *m_available,long long int *m_free);
Index: trunk/LMDZ.TITAN/libf/muphytitan/datasets.F90
===================================================================
--- trunk/LMDZ.TITAN/libf/muphytitan/datasets.F90	(revision 1896)
+++ trunk/LMDZ.TITAN/libf/muphytitan/datasets.F90	(revision 1897)
@@ -36,9 +36,5 @@
 !! summary: Dataset module definition file
 !! author: J. Burgalat
-!! date: 2014
-
-#if HAVE_CONFIG_H
-#include "config.h"
-#endif
+!! date: 2014,2017
 
 MODULE DATASETS 
@@ -65,21 +61,12 @@
   !! Note that for ASCII file, data must be ordered so first dimensions vary first. Same requirement 
   !! is needed for NetCDF file but in most cases, it is implicitly done (if dimensions are ordered).
-  USE LINT_PREC
-#if HAVE_NC_FTN
   USE NETCDF
-#endif
   IMPLICIT NONE
 
   PRIVATE 
 
-  PUBLIC :: read_dset, clear_dset, is_in, debug
+  PUBLIC :: read_dset, write_dset, clear_dset, is_in, has_data, debug
 
   LOGICAL :: debug = .false.  !! A control flag to enable verbose mode
-
-#if HAVE_NC_FTN
-  LOGICAL, PUBLIC, PARAMETER ::nc_supported = .true. !! NetCDF input files are supported
-#else
-  LOGICAL, PUBLIC, PARAMETER ::nc_supported = .false. !! NetCDF input files are not supported
-#endif
 
   !> Initialize a data set from either an ASCII or a NetCDF file
@@ -91,11 +78,14 @@
   !! Netcdf reader interface is available only if the library has been compiled with NetCDF support.
   INTERFACE read_dset 
-#if HAVE_NC_FTN
     MODULE PROCEDURE ncdf_rd_1d,ncdf_rd_2d,ncdf_rd_3d,ncdf_rd_4d,ncdf_rd_5d
 #if HAVE_NC4_FTN
     MODULE PROCEDURE ncdf4_rd_1d,ncdf4_rd_2d,ncdf4_rd_3d,ncdf4_rd_4d,ncdf4_rd_5d
 #endif
-#endif
     MODULE PROCEDURE ascii_rd_1d,ascii_rd_2d,ascii_rd_3d,ascii_rd_4d,ascii_rd_5d
+  END INTERFACE
+
+  !> Write a dataset in a netcdf (classic) file.
+  INTERFACE write_dset 
+    MODULE PROCEDURE nc_wr_1d,nc_wr_2d,nc_wr_3d,nc_wr_4d,nc_wr_5d
   END INTERFACE
 
@@ -111,5 +101,4 @@
 
   !> Private interface to netcdf informations getters
-#if HAVE_NC_FTN
   INTERFACE get_nc_info
     MODULE PROCEDURE get_nc3_info
@@ -118,44 +107,68 @@
 #endif
   END INTERFACE
-#endif
+
+  INTERFACE has_data
+    MODULE PROCEDURE has_d_1d,has_d_2d,has_d_3d,has_d_4d,has_d_5d
+  END INTERFACE
+
 
   TYPE, PUBLIC :: DSET1D
     !! A 1D data set
-    REAL(kind=wp), DIMENSION(:), ALLOCATABLE :: x    !! X coordinate tabulated values
-    REAL(kind=wp), DIMENSION(:), ALLOCATABLE :: data !! Tabulated function's value at each coordinate
+    REAL(kind=8), DIMENSION(:), ALLOCATABLE :: x              !! X coordinate tabulated values
+    REAL(kind=8), DIMENSION(:), ALLOCATABLE :: data           !! Tabulated function's value at each coordinate
+    CHARACTER(len=NF90_MAX_NAME)            :: xname = "X"    !! Name of the X coordinate
+    CHARACTER(len=NF90_MAX_NAME)            :: dname = "data" !! Name of the data block
   END TYPE DSET1D
 
   TYPE, PUBLIC :: DSET2D
     !! A 2D data set
-    REAL(kind=wp), DIMENSION(:), ALLOCATABLE   :: x    !! X coordinate tabulated values
-    REAL(kind=wp), DIMENSION(:), ALLOCATABLE   :: y    !! Y coordinate tabulated values
-    REAL(kind=wp), DIMENSION(:,:), ALLOCATABLE :: data !! Tabulated function's value at each coordinate
+    REAL(kind=8), DIMENSION(:), ALLOCATABLE   :: x              !! X coordinate tabulated values
+    REAL(kind=8), DIMENSION(:), ALLOCATABLE   :: y              !! Y coordinate tabulated values
+    REAL(kind=8), DIMENSION(:,:), ALLOCATABLE :: data           !! Tabulated function's value at each coordinate
+    CHARACTER(len=NF90_MAX_NAME)              :: xname = "X"    !! Name of the X coordinate
+    CHARACTER(len=NF90_MAX_NAME)              :: yname = "Y"    !! Name of the Y coordinate
+    CHARACTER(len=NF90_MAX_NAME)              :: dname = "data" !! Name of the data block
   END TYPE DSET2D
 
   TYPE, PUBLIC :: DSET3D
     !! A 3D data set
-    REAL(kind=wp), DIMENSION(:), ALLOCATABLE     :: x    !! X coordinate tabulated values
-    REAL(kind=wp), DIMENSION(:), ALLOCATABLE     :: y    !! Y coordinate tabulated values
-    REAL(kind=wp), DIMENSION(:), ALLOCATABLE     :: z    !! Z coordinate tabulated values
-    REAL(kind=wp), DIMENSION(:,:,:), ALLOCATABLE :: data !! Tabulated function's value at each coordinate
+    REAL(kind=8), DIMENSION(:), ALLOCATABLE     :: x              !! X coordinate tabulated values
+    REAL(kind=8), DIMENSION(:), ALLOCATABLE     :: y              !! Y coordinate tabulated values
+    REAL(kind=8), DIMENSION(:), ALLOCATABLE     :: z              !! Z coordinate tabulated values
+    REAL(kind=8), DIMENSION(:,:,:), ALLOCATABLE :: data           !! Tabulated function's value at each coordinate
+    CHARACTER(len=NF90_MAX_NAME)                :: xname = "X"    !! Name of the X coordinate
+    CHARACTER(len=NF90_MAX_NAME)                :: yname = "Y"    !! Name of the Y coordinate
+    CHARACTER(len=NF90_MAX_NAME)                :: zname = "Z"    !! Name of the Z coordinate
+    CHARACTER(len=NF90_MAX_NAME)                :: dname = "data" !! Name of the data block
   END TYPE DSET3D
 
   TYPE, PUBLIC :: DSET4D
     !! A 4D data set
-    REAL(kind=wp), DIMENSION(:), ALLOCATABLE       :: x    !! X coordinate tabulated values
-    REAL(kind=wp), DIMENSION(:), ALLOCATABLE       :: y    !! Y coordinate tabulated values
-    REAL(kind=wp), DIMENSION(:), ALLOCATABLE       :: z    !! Z coordinate tabulated values
-    REAL(kind=wp), DIMENSION(:), ALLOCATABLE       :: t    !! T coordinate tabulated values
-    REAL(kind=wp), DIMENSION(:,:,:,:), ALLOCATABLE :: data !! Tabulated function's value at each coordinate
+    REAL(kind=8), DIMENSION(:), ALLOCATABLE       :: x              !! X coordinate tabulated values
+    REAL(kind=8), DIMENSION(:), ALLOCATABLE       :: y              !! Y coordinate tabulated values
+    REAL(kind=8), DIMENSION(:), ALLOCATABLE       :: z              !! Z coordinate tabulated values
+    REAL(kind=8), DIMENSION(:), ALLOCATABLE       :: t              !! T coordinate tabulated values
+    REAL(kind=8), DIMENSION(:,:,:,:), ALLOCATABLE :: data           !! Tabulated function's value at each coordinate
+    CHARACTER(len=NF90_MAX_NAME)                  :: xname = "X"    !! Name of the X coordinate
+    CHARACTER(len=NF90_MAX_NAME)                  :: yname = "Y"    !! Name of the Y coordinate
+    CHARACTER(len=NF90_MAX_NAME)                  :: zname = "Z"    !! Name of the Z coordinate
+    CHARACTER(len=NF90_MAX_NAME)                  :: tname = "T"    !! Name of the T coordinate
+    CHARACTER(len=NF90_MAX_NAME)                  :: dname = "data" !! Name of the data block
   END TYPE DSET4D
 
   TYPE, PUBLIC :: DSET5D
     !! A 5D data set
-    REAL(kind=wp), DIMENSION(:), ALLOCATABLE         :: x    !! X coordinate tabulated values
-    REAL(kind=wp), DIMENSION(:), ALLOCATABLE         :: y    !! Y coordinate tabulated values
-    REAL(kind=wp), DIMENSION(:), ALLOCATABLE         :: z    !! Z coordinate tabulated values
-    REAL(kind=wp), DIMENSION(:), ALLOCATABLE         :: t    !! T coordinate tabulated values
-    REAL(kind=wp), DIMENSION(:), ALLOCATABLE         :: w    !! W coordinate tabulated values
-    REAL(kind=wp), DIMENSION(:,:,:,:,:), ALLOCATABLE :: data !! Tabulated function's value at each coordinate
+    REAL(kind=8), DIMENSION(:), ALLOCATABLE         :: x              !! X coordinate tabulated values
+    REAL(kind=8), DIMENSION(:), ALLOCATABLE         :: y              !! Y coordinate tabulated values
+    REAL(kind=8), DIMENSION(:), ALLOCATABLE         :: z              !! Z coordinate tabulated values
+    REAL(kind=8), DIMENSION(:), ALLOCATABLE         :: t              !! T coordinate tabulated values
+    REAL(kind=8), DIMENSION(:), ALLOCATABLE         :: w              !! W coordinate tabulated values
+    REAL(kind=8), DIMENSION(:,:,:,:,:), ALLOCATABLE :: data           !! Tabulated function's value at each coordinate
+    CHARACTER(len=NF90_MAX_NAME)                    :: xname = "X"    !! Name of the X coordinate
+    CHARACTER(len=NF90_MAX_NAME)                    :: yname = "Y"    !! Name of the Y coordinate
+    CHARACTER(len=NF90_MAX_NAME)                    :: zname = "Z"    !! Name of the Z coordinate
+    CHARACTER(len=NF90_MAX_NAME)                    :: tname = "T"    !! Name of the T coordinate
+    CHARACTER(len=NF90_MAX_NAME)                    :: wname = "W"    !! Name of the W coordinate
+    CHARACTER(len=NF90_MAX_NAME)                    :: dname = "data" !! Name of the data block
   END TYPE DSET5D
 
@@ -165,8 +178,8 @@
   FUNCTION is_in_1d(set,x) RESULT(ret)
     !! Check if point is in the 1D data set
-    TYPE(DSET1D), INTENT(in)  :: set !! Dataset object to search in
-    REAL(kind=wp), INTENT(in) :: x   !! coordinate of the point to check
+    TYPE(DSET1D), INTENT(in) :: set !! Dataset object to search in
+    REAL(kind=8), INTENT(in) :: x   !! coordinate of the point to check
     LOGICAL :: ret                   !! .true. if the point is in the data set, .false. otherwise
-    REAL(kind=wp) :: l,u
+    REAL(kind=8) :: l,u
     ret=.true.
     l  = set%x(1) ; u= set%x(size(set%x))
@@ -177,9 +190,9 @@
   FUNCTION is_in_2d(set,x,y) RESULT(ret)
     !! Check if point is in the 2D data set
-    TYPE(DSET2D), INTENT(in)  :: set !! Dataset object to search in
-    REAL(kind=wp), INTENT(in) :: x   !! X coordinate of the point to check
-    REAL(kind=wp), INTENT(in) :: y   !! Y coordinate of the point to check
+    TYPE(DSET2D), INTENT(in) :: set !! Dataset object to search in
+    REAL(kind=8), INTENT(in) :: x   !! X coordinate of the point to check
+    REAL(kind=8), INTENT(in) :: y   !! Y coordinate of the point to check
     LOGICAL :: ret                   !! .true. if the point is in the data set, .false. otherwise
-    REAL(kind=wp) :: l,u
+    REAL(kind=8) :: l,u
     ret=.false.
     l  = set%x(1) ; u= set%x(size(set%x))
@@ -194,10 +207,10 @@
   FUNCTION is_in_3d(set,x,y,z) RESULT(ret)
     !! Check if point is in the 3D data set
-    TYPE(DSET3D), INTENT(in)  :: set !! Dataset object to search in
-    REAL(kind=wp), INTENT(in) :: x   !! X coordinate of the point to check
-    REAL(kind=wp), INTENT(in) :: y   !! Y coordinate of the point to check
-    REAL(kind=wp), INTENT(in) :: z   !! Z coordinate of the point to check
+    TYPE(DSET3D), INTENT(in) :: set !! Dataset object to search in
+    REAL(kind=8), INTENT(in) :: x   !! X coordinate of the point to check
+    REAL(kind=8), INTENT(in) :: y   !! Y coordinate of the point to check
+    REAL(kind=8), INTENT(in) :: z   !! Z coordinate of the point to check
     LOGICAL :: ret                   !! .true. if the point is in the data set, .false. otherwise
-    REAL(kind=wp) :: l,u
+    REAL(kind=8) :: l,u
     ret=.false.
     l  = set%x(1) ; u= set%x(size(set%x))
@@ -213,11 +226,11 @@
   FUNCTION is_in_4d(set,x,y,z,t) RESULT(ret)
     !! Check if point is in the 4D data set
-    TYPE(DSET4D), INTENT(in)  :: set !! Dataset object to search in
-    REAL(kind=wp), INTENT(in) :: x   !! X coordinate of the point to check
-    REAL(kind=wp), INTENT(in) :: y   !! Y coordinate of the point to check
-    REAL(kind=wp), INTENT(in) :: z   !! Z coordinate of the point to check
-    REAL(kind=wp), INTENT(in) :: t   !! T coordinate of the point to check
+    TYPE(DSET4D), INTENT(in) :: set !! Dataset object to search in
+    REAL(kind=8), INTENT(in) :: x   !! X coordinate of the point to check
+    REAL(kind=8), INTENT(in) :: y   !! Y coordinate of the point to check
+    REAL(kind=8), INTENT(in) :: z   !! Z coordinate of the point to check
+    REAL(kind=8), INTENT(in) :: t   !! T coordinate of the point to check
     LOGICAL :: ret                   !! .true. if the point is in the data set, .false. otherwise
-    REAL(kind=wp) :: l,u
+    REAL(kind=8) :: l,u
     ret=.false.
     l  = set%x(1) ; u= set%x(size(set%x))
@@ -235,12 +248,12 @@
   FUNCTION is_in_5d(set,x,y,z,t,w) RESULT(ret)
     !! Check if point is in the 4D data set
-    TYPE(DSET5D), INTENT(in)  :: set !! Dataset object to search in
-    REAL(kind=wp), INTENT(in) :: x   !! X coordinate of the point to check
-    REAL(kind=wp), INTENT(in) :: y   !! Y coordinate of the point to check
-    REAL(kind=wp), INTENT(in) :: z   !! Z coordinate of the point to check
-    REAL(kind=wp), INTENT(in) :: t   !! T coordinate of the point to check
-    REAL(kind=wp), INTENT(in) :: w   !! W coordinate of the point to check
+    TYPE(DSET5D), INTENT(in) :: set !! Dataset object to search in
+    REAL(kind=8), INTENT(in) :: x   !! X coordinate of the point to check
+    REAL(kind=8), INTENT(in) :: y   !! Y coordinate of the point to check
+    REAL(kind=8), INTENT(in) :: z   !! Z coordinate of the point to check
+    REAL(kind=8), INTENT(in) :: t   !! T coordinate of the point to check
+    REAL(kind=8), INTENT(in) :: w   !! W coordinate of the point to check
     LOGICAL :: ret                   !! .true. if the point is in the data set, .false. otherwise
-    REAL(kind=wp) :: l,u
+    REAL(kind=8) :: l,u
     ret=.false.
     l  = set%x(1) ; u= set%x(size(set%x))
@@ -300,52 +313,78 @@
   END FUNCTION ascii_header
 
-#if HAVE_NC_FTN
-  FUNCTION nc_get_dim(fid,did,var,values,ds) RESULT(ret)
-    !! Get dimensions information of a NetCDF variable 
+  FUNCTION nc_get_dim_by_name(fid,dn,values,ds,did) RESULT(ret)
+    !! Get informations about a dimension.
     !!
     !! The method gets the values and size of a given dimension.
-    INTEGER, INTENT(in)                                   :: fid
+    !!
+    !! Errors occur if:
+    !!
+    !! - the given name is not a dimension name.
+    !! - the size of the dimension is less than 2 (the method assumes dimension has an afferent variable with values).
+    !! - the method can not retrieve the values of the afferent variable.
+    INTEGER, INTENT(in)                                  :: fid
       !! Id of the NetCDF file
-    INTEGER, INTENT(in)                                   :: did
+    CHARACTER(len=NF90_MAX_NAME), INTENT(in)             :: dn
+      !! Name of the dimension.
+    REAL(kind=8), INTENT(out), DIMENSION(:), ALLOCATABLE :: values
+      !! Values of the dimension
+    INTEGER, INTENT(out)                                 :: ds 
+      !! Size of __values__
+    INTEGER, INTENT(out)                                 :: did
       !! Id of the NetCDF dimension 
-    CHARACTER(len=*), INTENT(in)                          :: var
-      !! Name of the related NetCDF variable
-    REAL(kind=wp), INTENT(out), DIMENSION(:), ALLOCATABLE :: values
-      !! Values of the dimension
-    INTEGER, INTENT(out)                                  :: ds 
-      !! Size of __values__
     LOGICAL :: ret
       !! .true. if no error(s) occured, .false. otherwise
     INTEGER                      :: vid,err
-    CHARACTER(len=NF90_MAX_NAME) :: dn
     CHARACTER(len=15)            :: i2s
     ret = .false.
     ! --- Get dimension informations
-    IF (NF90_INQUIRE_DIMENSION(fid,did,dn,ds) /= NF90_NOERR) THEN
-      IF (debug) THEN
-        WRITE(i2s,*) did ; i2s=TRIM(i2s)
-        WRITE(*,'(a)') "ERROR:"//TRIM(var)//": Cannot find "// &
-                 "dimension #"//TRIM(i2s)//" name"
-      ENDIF
-      err = NF90_CLOSE(fid) ; RETURN
-    ENDIF
-    IF (ds < 2) THEN
-      IF (debug) WRITE(*,'(a)') "ERROR:"//TRIM(dn)//": Invalid dimension "// &
-                 "size (<2)"
-      err = NF90_CLOSE(fid) ; RETURN
-    ENDIF
-    IF (NF90_INQ_VARID(fid,TRIM(dn),vid) /= NF90_NOERR) THEN
-      IF (debug) WRITE(*,'(a)') "ERROR:"//TRIM(dn)//": Cannot get ID"
-      err = NF90_CLOSE(fid) ; RETURN
-    ENDIF
+    err = NF90_INQ_DIMID(fid,dn,did)
+    IF (err /= NF90_NOERR) RETURN
+    err = NF90_INQUIRE_DIMENSION(fid,did,len=ds)
+    IF (err /= NF90_NOERR) RETURN
+    IF (ds < 2) RETURN
+    err = NF90_INQ_VARID(fid,TRIM(dn),vid)
+    IF (err /= NF90_NOERR) RETURN
     ALLOCATE(values(ds))
-    IF (NF90_GET_VAR(fid,vid,values) /= NF90_NOERR) THEN
-      IF (debug) WRITE(*,'(a)') "ERROR:"//TRIM(dn)//": Cannot get values"
-      err = NF90_CLOSE(fid) ; RETURN
-    ENDIF
-    ret = .true.
-  END FUNCTION nc_get_dim
-
-  FUNCTION get_nc3_info(path,variable,ncid,vid,dimids) RESULT(ret)
+    err = NF90_GET_VAR(fid,vid,values)
+    IF (err /= NF90_NOERR) RETURN
+    ret = .true.
+  END FUNCTION nc_get_dim_by_name
+
+  FUNCTION nc_get_dim_by_id(fid,did,values,ds,dn) RESULT(ret)
+    !! Get informations about a dimension.
+    !!
+    !! The method gets the values and size of a given dimension.
+    !!
+    !! Errors occur if:
+    !!
+    !! - the given id is not a dimension identifier.
+    !! - the size of the dimension is less than 2 (the method assumes dimension has an afferent variable with values).
+    !! - the method can not retrieve the values of the afferent variable.
+    INTEGER, INTENT(in)                                  :: fid
+      !! Id of the NetCDF file
+    INTEGER, INTENT(in)                                  :: did
+      !! Id of the NetCDF dimension 
+    REAL(kind=8), INTENT(out), DIMENSION(:), ALLOCATABLE :: values
+      !! Values of the dimension
+    INTEGER, INTENT(out)                                 :: ds 
+      !! Size of __values__
+    CHARACTER(len=NF90_MAX_NAME), INTENT(out)            :: dn
+      !! Name of the dimension.
+    LOGICAL :: ret
+      !! .true. if no error(s) occured, .false. otherwise
+    INTEGER                      :: vid,err
+    CHARACTER(len=15)            :: i2s
+    ret = .false.
+    ! --- Get dimension informations
+    IF (NF90_INQUIRE_DIMENSION(fid,did,dn,ds) /= NF90_NOERR) RETURN
+    IF (ds < 2) RETURN
+    IF (NF90_INQ_VARID(fid,TRIM(dn),vid) /= NF90_NOERR) RETURN
+    ALLOCATE(values(ds))
+    IF (NF90_GET_VAR(fid,vid,values) /= NF90_NOERR) RETURN
+    ret = .true.
+  END FUNCTION nc_get_dim_by_id
+
+  FUNCTION get_nc3_info(path,variable,ncid,vid,dimids,verbose) RESULT(ret)
     !! Get variable informations from NetCDF file
     !! 
@@ -358,6 +397,6 @@
     !! - variable's dimensions sizes
     !!
-    !! If no error occured (i.e. the function has returned .true.) then the
-    !! NetCDF file remains open. In other case, the file is closed.
+    !! The method always opens and closes the file. If the file cannot be opened,
+    !! __ncid__ is set to -1.
     CHARACTER(len=*), INTENT(in)                    :: path
       !! Path of the NetCDF file
@@ -370,11 +409,16 @@
     INTEGER, INTENT(out), DIMENSION(:), ALLOCATABLE :: dimids
       !! Id of the variable's dimensions. __dimids__ is not allocated on error.
+    LOGICAL, INTENT(in), OPTIONAL                   :: verbose
+      !! True to print out message on error (default to False).
     LOGICAL :: ret
       !! .true. if no errors occured, .false. otherwise
     INTEGER :: fid,ty,nc,err
-    ret = .false.
+    LOGICAL :: zlog
+    zlog = .false. ; IF (PRESENT(verbose)) zlog = verbose
+    ret = .false.
+    ncid = -1
     ! Opens file
     IF (NF90_OPEN(TRIM(path),NF90_NOWRITE,fid) /= NF90_NOERR) THEN
-      WRITE(*,'(a)') 'ERROR: Cannot open '//trim(path)
+      IF (zlog) WRITE(*,'(a)') 'ERROR: Cannot open '//trim(path)
       RETURN
     ENDIF
@@ -382,5 +426,5 @@
     ! Searches for variable
     IF (NF90_INQ_VARID(ncid,TRIM(variable),vid) /= NF90_NOERR) THEN
-      WRITE(*,'(a)') 'Cannot find '//TRIM(variable)//' in '//trim(path)
+      IF (zlog) WRITE(*,'(a)') 'Cannot find '//TRIM(variable)//' in '//trim(path)
       nc = NF90_CLOSE(fid)
       RETURN
@@ -389,5 +433,5 @@
     ! 1st call to get type and number of dimensions)
     IF (NF90_INQUIRE_VARIABLE(ncid,vid,xtype=ty,ndims=nc) /= NF90_NOERR) THEN
-      WRITE(*,'(a)') 'Cannot access to '//TRIM(variable)//' informations'
+      IF (zlog) WRITE(*,'(a)') 'Cannot access to '//TRIM(variable)//' informations'
       nc = NF90_CLOSE(fid)
       RETURN
@@ -395,5 +439,5 @@
       ! Checks type
       IF (ty == NF90_CHAR) THEN
-        WRITE(*,'(a)') 'Inconsistent variable type (should be numeric)'
+        IF (zlog) WRITE(*,'(a)') 'Inconsistent variable type (should be numeric)'
         nc = NF90_CLOSE(fid)
         RETURN
@@ -404,14 +448,15 @@
     ! first get dimensions id
     IF (NF90_INQUIRE_VARIABLE(ncid,vid,dimids=dimids) /= NF90_NOERR) THEN
-      WRITE(*,'(a)') 'Cannot access to '//TRIM(variable)//' informations'
+      IF (zlog) WRITE(*,'(a)') 'Cannot access to '//TRIM(variable)//' informations'
       nc = NF90_CLOSE(fid)
       DEALLOCATE(dimids)
       RETURN
     ENDIF
+    nc = NF90_CLOSE(fid)
     ret = .true. 
   END FUNCTION get_nc3_info
 
 #if HAVE_NC4_FTN 
-  FUNCTION get_nc4_info(path,variable,group,ncid,vid,dimids) RESULT(ret)
+  FUNCTION get_nc4_info(path,variable,group,ncid,vid,dimids,verbose) RESULT(ret)
     !! Get variable informations from NetCDF4 file
     !!
@@ -424,6 +469,6 @@
     !! - variable's dimensions sizes
     !!
-    !! If no error occured (i.e. the function has returned .true.) then the
-    !! NetCDF file remains open. In other case, the file is closed.
+    !! The method always opens and closes the file. If the file cannot be opened,
+    !! __ncid__ is set to -1.
     CHARACTER(len=*), INTENT(in)                    :: path
       !! Path of the NetCDF file
@@ -438,11 +483,16 @@
     INTEGER, INTENT(out), DIMENSION(:), ALLOCATABLE :: dimids
       !! Id of the variable's dimensions. On error, __dimids__ is not allocated. 
+    LOGICAL, INTENT(in), OPTIONAL                   :: verbose
+      !! True to print out message on error (default to False).
     LOGICAL :: ret
       !! .true. if no errors occured, .false. otherwise
     INTEGER :: fid,ty,nc,err
-    ret = .false.
+    LOGICAL :: zlog
+    zlog = .false. ; IF (PRESENT(verbose)) zlog = verbose
+    ret = .false.
+    ncid = -1
     ! Opens file
     IF (NF90_OPEN(TRIM(path),NF90_NOWRITE,fid) /= NF90_NOERR) THEN
-      WRITE(*,'(a)') 'ERROR: Cannot open '//trim(path)
+      IF (zlog) WRITE(*,'(a)') 'ERROR: Cannot open '//trim(path)
       RETURN
     ENDIF
@@ -456,16 +506,16 @@
       ! NF90_ENOGRP is missing from Netcdf-Fortran4.2 : its value=-125
       CASE(-125)
-        WRITE(*,'(a)') TRIM(group)//' does not exist in '//TRIM(path)
+        IF (zlog) WRITE(*,'(a)') TRIM(group)//' does not exist in '//TRIM(path)
         nc = NF90_CLOSE(fid) ; RETURN
       CASE(NF90_ENOTNC4,NF90_ESTRICTNC3)
-        WRITE(*,'(a)') TRIM(path)//' is not a NetCDF-4 file with "group" feature'
+        IF (zlog) WRITE(*,'(a)') TRIM(path)//' is not a NetCDF-4 file with "group" feature'
         nc = NF90_CLOSE(fid) ; RETURN
       CASE(NF90_EHDFERR)
-        WRITE(*,'(a)') "Too bad, an HDF5 error has been reported..."
+        IF (zlog) WRITE(*,'(a)') "Too bad, an HDF5 error has been reported..."
         nc = NF90_CLOSE(fid) ; RETURN
     END SELECT
     ! Searches for variable
     IF (NF90_INQ_VARID(ncid,TRIM(variable),vid) /= NF90_NOERR) THEN
-      WRITE(*,'(a)') 'Cannot find '//TRIM(variable)//' in '//trim(path)
+      IF (zlog) WRITE(*,'(a)') 'Cannot find '//TRIM(variable)//' in '//trim(path)
       nc = NF90_CLOSE(fid)
       RETURN
@@ -474,5 +524,5 @@
     ! 1st call to get type and number of dimensions)
     IF (NF90_INQUIRE_VARIABLE(ncid,vid,xtype=ty,ndims=nc) /= NF90_NOERR) THEN
-      WRITE(*,'(a)') 'Cannot access to '//TRIM(variable)//' informations'
+      IF (zlog) WRITE(*,'(a)') 'Cannot access to '//TRIM(variable)//' informations'
       nc = NF90_CLOSE(fid)
       RETURN
@@ -480,5 +530,5 @@
       ! Checks type
       IF (ty == NF90_CHAR) THEN
-        WRITE(*,'(a)') 'Inconsistent variable type (should be numeric)'
+        IF (zlog) WRITE(*,'(a)') 'Inconsistent variable type (should be numeric)'
         nc = NF90_CLOSE(fid)
         RETURN
@@ -489,20 +539,17 @@
     ! first get dimensions id
     IF (NF90_INQUIRE_VARIABLE(ncid,vid,dimids=dimids) /= NF90_NOERR) THEN
-      WRITE(*,'(a)') 'Cannot access to '//TRIM(variable)//' informations'
+      IF (zlog) WRITE(*,'(a)') 'Cannot access to '//TRIM(variable)//' informations'
       nc = NF90_CLOSE(fid)
       DEALLOCATE(dimids)
       RETURN
     ENDIF
+    nc = NF90_CLOSE(fid)
     ret = .true. 
   END FUNCTION get_nc4_info
 #endif
-#endif
 
   !-------------------------
   ! NetCDF data file readers
   !-------------------------
-
-#if HAVE_NC_FTN
-
 
   FUNCTION ncdf_rd_1d(path,variable,set) RESULT(ret)
@@ -512,5 +559,5 @@
     TYPE(DSET1D), INTENT(out)    :: set      !! Output dataset object
     LOGICAL :: ret                           !! .true. if no errors occured, .false. otherwise
-    INTEGER                            :: fi,vi,nd
+    INTEGER                            :: fi,vi,nd,iret
     INTEGER, DIMENSION(:), ALLOCATABLE :: di,ds
     CHARACTER(len=NF90_MAX_NAME)       :: dn
@@ -521,4 +568,5 @@
     ! --- Check NetCDF file info
     IF (.NOT.get_nc_info(path,variable,fi,vi,di)) RETURN
+    iret = NF90_OPEN(path,NF90_NOWRITE,fi)
     ! --- Check dimension size
     nd = SIZE(di)
@@ -533,6 +581,6 @@
     ALLOCATE(ds(nd))
     ! ------ X coordinate
-    IF (.NOT.nc_get_dim(fi,di(1),variable,set%x,ds(1))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(1),set%x,ds(1),set%xname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! --- Read data
@@ -543,4 +591,5 @@
     ENDIF
     nd = NF90_CLOSE(fi)
+    set%dname = variable
     ret = .true.
     RETURN
@@ -553,5 +602,5 @@
     TYPE(DSET2D), INTENT(out)    :: set      !! Output dataset object
     LOGICAL :: ret                           !! .true. if no errors occured, .false. otherwise
-    INTEGER                            :: fi,vi,nd
+    INTEGER                            :: fi,vi,nd,iret
     INTEGER, DIMENSION(:), ALLOCATABLE :: di,ds
     CHARACTER(len=15)                  :: i2s
@@ -561,4 +610,5 @@
     ! --- Check NetCDF file info
     IF (.NOT.get_nc_info(path,variable,fi,vi,di)) RETURN 
+    iret = NF90_OPEN(path,NF90_NOWRITE,fi)
     ! --- Check dimension size
     nd = SIZE(di)
@@ -573,10 +623,10 @@
     ALLOCATE(ds(nd))
     ! ------ X coordinate
-    IF (.NOT.nc_get_dim(fi,di(1),variable,set%x,ds(1))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(1),set%x,ds(1),set%xname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! ------ Y coordinate
-    IF (.NOT.nc_get_dim(fi,di(2),variable,set%y,ds(2))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(2),set%y,ds(2),set%yname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! --- Read data
@@ -587,4 +637,5 @@
     ENDIF
     nd = NF90_CLOSE(fi)
+    set%dname = variable
     ret = .true.
     RETURN
@@ -597,5 +648,5 @@
     TYPE(DSET3D), INTENT(out)    :: set      !! Output dataset object
     LOGICAL :: ret                           !! .true. if no errors occured, .false. otherwise
-    INTEGER                            :: fi,vi,nd
+    INTEGER                            :: fi,vi,nd,iret
     INTEGER, DIMENSION(:), ALLOCATABLE :: di,ds
     CHARACTER(len=15)                  :: i2s
@@ -605,4 +656,5 @@
     ! --- Check NetCDF file info
     IF (.NOT.get_nc_info(path,variable,fi,vi,di)) RETURN 
+    iret = NF90_OPEN(path,NF90_NOWRITE,fi)
     ! --- Check dimension size
     nd = SIZE(di)
@@ -617,14 +669,14 @@
     ALLOCATE(ds(nd))
     ! ------ X coordinate
-    IF (.NOT.nc_get_dim(fi,di(1),variable,set%x,ds(1))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(1),set%x,ds(1),set%xname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! ------ Y coordinate
-    IF (.NOT.nc_get_dim(fi,di(2),variable,set%y,ds(2))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(2),set%y,ds(2),set%yname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! ------ Z coordinate
-    IF (.NOT.nc_get_dim(fi,di(3),variable,set%z,ds(3))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(3),set%z,ds(3),set%zname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! --- Read data
@@ -635,4 +687,5 @@
     ENDIF
     nd = NF90_CLOSE(fi)
+    set%dname = variable
     ret = .true.
     RETURN
@@ -645,5 +698,5 @@
     TYPE(DSET4D), INTENT(out)    :: set      !! Output dataset object
     LOGICAL :: ret                           !! .true. if no errors occured, .false. otherwise
-    INTEGER                            :: fi,vi,nd
+    INTEGER                            :: fi,vi,nd,iret
     INTEGER, DIMENSION(:), ALLOCATABLE :: di,ds
     CHARACTER(len=15)                  :: i2s
@@ -653,4 +706,5 @@
     ! --- Check NetCDF file info
     IF (.NOT.get_nc_info(path,variable,fi,vi,di)) RETURN 
+    iret = NF90_OPEN(path,NF90_NOWRITE,fi)
     ! --- Check dimension size
     nd = SIZE(di)
@@ -665,18 +719,18 @@
     ALLOCATE(ds(nd))
     ! ------ X coordinate
-    IF (.NOT.nc_get_dim(fi,di(1),variable,set%x,ds(1))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(1),set%x,ds(1),set%xname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! ------ Y coordinate
-    IF (.NOT.nc_get_dim(fi,di(2),variable,set%y,ds(2))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(2),set%y,ds(2),set%yname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! ------ Z coordinate
-    IF (.NOT.nc_get_dim(fi,di(3),variable,set%z,ds(3))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(3),set%z,ds(3),set%zname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! ------ T coordinate
-    IF (.NOT.nc_get_dim(fi,di(4),variable,set%t,ds(4))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(4),set%t,ds(4),set%tname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! --- Read data
@@ -687,4 +741,5 @@
     ENDIF
     nd = NF90_CLOSE(fi)
+    set%dname = variable
     ret = .true.
     RETURN
@@ -697,5 +752,5 @@
     TYPE(DSET5D), INTENT(out)    :: set      !! Output dataset object
     LOGICAL :: ret                           !! .true. if no errors occured, .false. otherwise
-    INTEGER                            :: fi,vi,nd
+    INTEGER                            :: fi,vi,nd,iret
     INTEGER, DIMENSION(:), ALLOCATABLE :: di,ds
     CHARACTER(len=15)                  :: i2s
@@ -705,4 +760,5 @@
     ! --- Check NetCDF file info
     IF (.NOT.get_nc_info(path,variable,fi,vi,di)) RETURN
+    iret = NF90_OPEN(path,NF90_NOWRITE,fi)
     ! --- Check dimension size
     nd = SIZE(di)
@@ -717,22 +773,22 @@
     ALLOCATE(ds(nd))
     ! ------ X coordinate
-    IF (.NOT.nc_get_dim(fi,di(1),variable,set%x,ds(1))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(1),set%x,ds(1),set%xname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! ------ Y coordinate
-    IF (.NOT.nc_get_dim(fi,di(2),variable,set%y,ds(2))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(2),set%y,ds(2),set%yname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! ------ Z coordinate
-    IF (.NOT.nc_get_dim(fi,di(3),variable,set%z,ds(3))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(3),set%z,ds(3),set%zname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! ------ T coordinate
-    IF (.NOT.nc_get_dim(fi,di(4),variable,set%t,ds(4))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(4),set%t,ds(4),set%tname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! ------ W coordinate
-    IF (.NOT.nc_get_dim(fi,di(5),variable,set%w,ds(5))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(5),set%w,ds(5),set%wname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! --- Read data
@@ -743,4 +799,5 @@
     ENDIF
     nd = NF90_CLOSE(fi)
+    set%dname = variable
     ret = .true.
     RETURN
@@ -757,5 +814,5 @@
     TYPE(DSET1D), INTENT(out)    :: set      !! Output dataset
     LOGICAL :: ret                           !! .true. if no errors occured, .false. otherwise
-    INTEGER                            :: fi,vi,nd
+    INTEGER                            :: fi,vi,nd,iret
     INTEGER, DIMENSION(:), ALLOCATABLE :: di,ds
     CHARACTER(len=NF90_MAX_NAME)       :: dn
@@ -766,4 +823,5 @@
     ! --- Check NetCDF file info
     IF (.NOT.get_nc_info(path,group,variable,fi,vi,di)) RETURN 
+    iret = NF90_OPEN(path,NF90_NOWRITE,fi)
     ! --- Check dimension size
     nd = SIZE(di)
@@ -778,6 +836,6 @@
     ALLOCATE(ds(nd))
     ! ------ X coordinate
-    IF (.NOT.nc_get_dim(fi,di(1),variable,set%x,ds(1))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(1),set%x,ds(1),set%xname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! --- Read data
@@ -788,4 +846,5 @@
     ENDIF
     nd = NF90_CLOSE(fi)
+    set%dname = variable
     ret = .true.
     RETURN
@@ -799,5 +858,5 @@
     TYPE(DSET2D), INTENT(out)    :: set      !! Output dataset
     LOGICAL :: ret                           !! .true. if no errors occured, .false. otherwise
-    INTEGER                            :: fi,vi,nd
+    INTEGER                            :: fi,vi,nd,iret
     INTEGER, DIMENSION(:), ALLOCATABLE :: di,ds
     CHARACTER(len=15)                  :: i2s
@@ -807,4 +866,5 @@
     ! --- Check NetCDF file info
     IF (.NOT.get_nc_info(path,group,variable,fi,vi,di)) rETURN
+    iret = NF90_OPEN(path,NF90_NOWRITE,fi)
     ! --- Check dimension size
     nd = SIZE(di)
@@ -819,10 +879,10 @@
     ALLOCATE(ds(nd))
     ! ------ X coordinate
-    IF (.NOT.nc_get_dim(fi,di(1),variable,set%x,ds(1))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(1),set%x,ds(1),set%xname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! ------ Y coordinate
-    IF (.NOT.nc_get_dim(fi,di(2),variable,set%y,ds(2))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(2),set%y,ds(2),set%yname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! --- Read data
@@ -833,4 +893,5 @@
     ENDIF
     nd = NF90_CLOSE(fi)
+    set%dname = variable
     ret = .true.
     RETURN
@@ -844,5 +905,5 @@
     TYPE(DSET3D), INTENT(out)    :: set      !! Output dataset
     LOGICAL :: ret                           !! .true. if no errors occured, .false. otherwise
-    INTEGER                            :: fi,vi,nd
+    INTEGER                            :: fi,vi,nd,iret
     INTEGER, DIMENSION(:), ALLOCATABLE :: di,ds
     CHARACTER(len=15)                  :: i2s
@@ -852,4 +913,5 @@
     ! --- Check NetCDF file info
     IF (.NOT.get_nc_info(path,group,variable,fi,vi,di)) RETURN 
+    iret = NF90_OPEN(path,NF90_NOWRITE,fi)
     ! --- Check dimension size
     nd = SIZE(di)
@@ -864,14 +926,14 @@
     ALLOCATE(ds(nd))
     ! ------ X coordinate
-    IF (.NOT.nc_get_dim(fi,di(1),variable,set%x,ds(1))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(1),set%x,ds(1),set%xname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! ------ Y coordinate
-    IF (.NOT.nc_get_dim(fi,di(2),variable,set%y,ds(2))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(2),set%y,ds(2),set%yname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! ------ Z coordinate
-    IF (.NOT.nc_get_dim(fi,di(3),variable,set%z,ds(3))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(3),set%z,ds(3),set%zname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! --- Read data
@@ -882,4 +944,5 @@
     ENDIF
     nd = NF90_CLOSE(fi)
+    set%dname = variable
     ret = .true.
     RETURN
@@ -893,5 +956,5 @@
     TYPE(DSET4D), INTENT(out)    :: set      !! Output dataset
     LOGICAL :: ret                           !! .true. if no errors occured, .false. otherwise
-    INTEGER                            :: fi,vi,nd
+    INTEGER                            :: fi,vi,nd,iret
     INTEGER, DIMENSION(:), ALLOCATABLE :: di,ds
     CHARACTER(len=15)                  :: i2s
@@ -901,4 +964,5 @@
     ! --- Check NetCDF file info
     IF (.NOT.get_nc_info(path,group,variable,fi,vi,di)) RETURN 
+    iret = NF90_OPEN(path,NF90_NOWRITE,fi)
     ! --- Check dimension size
     nd = SIZE(di)
@@ -913,18 +977,18 @@
     ALLOCATE(ds(nd))
     ! ------ X coordinate
-    IF (.NOT.nc_get_dim(fi,di(1),variable,set%x,ds(1))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(1),set%x,ds(1),set%xname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! ------ Y coordinate
-    IF (.NOT.nc_get_dim(fi,di(2),variable,set%y,ds(2))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(2),set%y,ds(2),set%yname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! ------ Z coordinate
-    IF (.NOT.nc_get_dim(fi,di(3),variable,set%z,ds(3))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(3),set%z,ds(3),set%zname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! ------ T coordinate
-    IF (.NOT.nc_get_dim(fi,di(4),variable,set%t,ds(4))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(4),set%t,ds(4),set%tname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! --- Read data
@@ -935,4 +999,5 @@
     ENDIF
     nd = NF90_CLOSE(fi)
+    set%dname = variable
     ret = .true.
     RETURN
@@ -946,5 +1011,5 @@
     TYPE(DSET5D), INTENT(out)    :: set      !! Output dataset
     LOGICAL :: ret                           !! .true. if no errors occured, .false. otherwise
-    INTEGER                            :: fi,vi,nd
+    INTEGER                            :: fi,vi,nd,iret
     INTEGER, DIMENSION(:), ALLOCATABLE :: di,ds
     CHARACTER(len=15)                  :: i2s
@@ -954,4 +1019,5 @@
     ! --- Check NetCDF file info
     IF (.NOT.get_nc_info(path,group,variable,fi,vi,di)) RETURN
+    iret = NF90_OPEN(path,NF90_NOWRITE,fi)
     ! --- Check dimension size
     nd = SIZE(di)
@@ -966,21 +1032,22 @@
     ALLOCATE(ds(nd))
     ! ------ X coordinate
-    IF (.NOT.nc_get_dim(fi,di(1),variable,set%x,ds(1))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(1),set%x,ds(1),set%xname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! ------ Y coordinate
-    IF (.NOT.nc_get_dim(fi,di(2),variable,set%y,ds(2))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(2),set%y,ds(2),set%yname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! ------ Z coordinate
-    IF (.NOT.nc_get_dim(fi,di(3),variable,set%z,ds(3))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(3),set%z,ds(3),set%zname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! ------ T coordinate
-    IF (.NOT.nc_get_dim(fi,di(4),variable,set%t,ds(4))) THEN
-      CALL clear_dset(set) ; RETURN
+    IF (.NOT.nc_get_dim_by_id(fi,di(4),set%t,ds(4),set%tname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
     ENDIF
     ! ------ W coordinate
-    IF (.NOT.nc_get_dim(fi,di(5),variable,set%w,ds(5))) THEN
+    IF (.NOT.nc_get_dim_by_id(fi,di(5),set%w,ds(5),set%wname)) THEN
+      nd = NF90_CLOSE(fi) ; CALL clear_dset(set) ; RETURN
       CALL clear_dset(set) ; RETURN
     ENDIF
@@ -992,8 +1059,8 @@
     ENDIF
     nd = NF90_CLOSE(fi)
+    set%dname = variable
     ret = .true.
     RETURN
   END FUNCTION ncdf4_rd_5d
-#endif
 #endif
 
@@ -1007,7 +1074,7 @@
     TYPE(dset1d), INTENT(out)    :: set  !! output 1D dataset
     LOGICAL :: ret                       !! .true. if no error occured, .false. otherwise
-    INTEGER                     :: i,e
-    REAL(kind=wp), DIMENSION(2) :: vl
-    INTEGER, DIMENSION(1)       :: cc,ds,dp
+    INTEGER                    :: i,e
+    REAL(kind=8), DIMENSION(2) :: vl
+    INTEGER, DIMENSION(1)      :: cc,ds,dp
     ret = .false.
     CALL clear_dset(set)
@@ -1037,7 +1104,7 @@
     TYPE(dset2d), INTENT(out)    :: set  !! output 2D dataset
     LOGICAL :: ret                       !! .true. if no error occured, .false. otherwise
-    INTEGER                     :: i,e
-    REAL(kind=wp), DIMENSION(3) :: vl 
-    INTEGER, DIMENSION(2)       :: cc,ds,dp
+    INTEGER                    :: i,e
+    REAL(kind=8), DIMENSION(3) :: vl 
+    INTEGER, DIMENSION(2)      :: cc,ds,dp
     ret = .false.
     CALL clear_dset(set)
@@ -1077,7 +1144,7 @@
     TYPE(dset3d), INTENT(out)    :: set  !! output 3D dataset
     LOGICAL :: ret                       !! .true. if no error occured, .false. otherwise
-    INTEGER                     :: i,e
-    REAL(kind=wp), DIMENSION(4) :: vl 
-    INTEGER, DIMENSION(3)       :: cc,ds,dp
+    INTEGER                    :: i,e
+    REAL(kind=8), DIMENSION(4) :: vl 
+    INTEGER, DIMENSION(3)      :: cc,ds,dp
     ret = .false.
     CALL clear_dset(set)
@@ -1120,7 +1187,7 @@
     TYPE(dset4d), INTENT(out)    :: set  !! output 4D dataset
     LOGICAL :: ret                       !! .true. if no error occured, .false. otherwise
-    INTEGER                     :: i,e
-    REAL(kind=wp), DIMENSION(5) :: vl 
-    INTEGER, DIMENSION(4)       :: cc,ds,dp
+    INTEGER                    :: i,e
+    REAL(kind=8), DIMENSION(5) :: vl 
+    INTEGER, DIMENSION(4)      :: cc,ds,dp
     ret = .false.
     CALL clear_dset(set)
@@ -1166,7 +1233,7 @@
     TYPE(dset5d), INTENT(out)    :: set  !! output 5D dataset
     LOGICAL :: ret                       !! .true. if no error occured, .false. otherwise
-    INTEGER                     :: i,e
-    REAL(kind=wp), DIMENSION(6) :: vl 
-    INTEGER, DIMENSION(5)       :: cc,ds,dp
+    INTEGER                    :: i,e
+    REAL(kind=8), DIMENSION(6) :: vl 
+    INTEGER, DIMENSION(5)      :: cc,ds,dp
     ret = .false.
     CALL clear_dset(set)
@@ -1260,3 +1327,408 @@
   END SUBROUTINE clr_5d_set
 
+  FUNCTION has_d_1d(dset) RESULT(yes)
+    !! Check whether or not the dataset has data.
+    TYPE(DSET1D), INTENT(in)     :: dset !! Dataset to check
+    LOGICAL                      :: yes  !! return status
+    yes =  (ALLOCATED(dset%data).AND. &
+            ALLOCATED(dset%x))
+  END FUNCTION has_d_1d
+
+  FUNCTION has_d_2d(dset) RESULT(yes)
+    !! Check whether or not the dataset has data.
+    TYPE(DSET2D), INTENT(in)     :: dset !! Dataset to check
+    LOGICAL                      :: yes  !! return status
+    yes =  (ALLOCATED(dset%data).AND. &
+            ALLOCATED(dset%x)   .AND. &   
+            ALLOCATED(dset%y))
+  END FUNCTION has_d_2d
+
+  FUNCTION has_d_3d(dset) RESULT(yes)
+    !! Check whether or not the dataset has data.
+    TYPE(DSET3D), INTENT(in)     :: dset !! Dataset to check
+    LOGICAL                      :: yes  !! return status
+    yes =  (ALLOCATED(dset%data).AND. &
+            ALLOCATED(dset%x)   .AND. &   
+            ALLOCATED(dset%y)   .AND. &
+            ALLOCATED(dset%z))
+  END FUNCTION has_d_3d
+
+  FUNCTION has_d_4d(dset) RESULT(yes)
+    !! Check whether or not the dataset has data.
+    TYPE(DSET4D), INTENT(in)     :: dset !! Dataset to check
+    LOGICAL                      :: yes  !! return status
+    yes =  (ALLOCATED(dset%data).AND. &
+            ALLOCATED(dset%x)   .AND. &   
+            ALLOCATED(dset%y)   .AND. &
+            ALLOCATED(dset%z)   .AND. &
+            ALLOCATED(dset%t))
+  END FUNCTION has_d_4d
+
+  FUNCTION has_d_5d(dset) RESULt(yes)
+    !! Check whether or not the dataset has data.
+    TYPE(DSET5D), INTENT(in)     :: dset !! Dataset to check
+    LOGICAL                      :: yes  !! return status
+    yes =  (ALLOCATED(dset%data).AND. &
+            ALLOCATED(dset%x)   .AND. &   
+            ALLOCATED(dset%y)   .AND. &
+            ALLOCATED(dset%z)   .AND. &
+            ALLOCATED(dset%t)   .AND. &
+            ALLOCATED(dset%w)) 
+  END FUNCTION has_d_5d
+
+  FUNCTION nc_wr_1d(path,dset) RESULT(ret)
+    CHARACTER(len=*), INTENT(in) :: path !! Path of the netcdf file.
+    TYPE(DSET1D), INTENT(in)     :: dset !! Dataset to write
+    LOGICAL                      :: ret  !! Return status
+    INTEGER                                 :: fi,vi,nd,ds,iret
+    INTEGER, DIMENSION(:), ALLOCATABLE      :: di
+    REAL(kind=8), DIMENSION(:), ALLOCATABLE :: tv
+    CHARACTER(len=15)                       :: i2s
+    LOGICAL                                 :: hxd,ok
+    INTEGER                                 :: xid,vid 
+    ret = has_data(dset)
+    IF (.NOT.ret) RETURN
+    ret = .false.
+    INQUIRE(FILE=TRIM(path),EXIST=ok)
+    IF (ok) THEN
+      IF (NF90_OPEN(TRIM(path), NF90_WRITE, fi) /= NF90_NOERR) RETURN
+    ELSE
+      IF (NF90_CREATE(TRIM(path), NF90_NOCLOBBER, fi) /= NF90_NOERR) RETURN
+    ENDIF
+    iret = NF90_CLOSE(fi)
+    ! if variable already exist: get out of here !
+    IF (get_nc_info(path,dset%dname,fi,vi,di)) RETURN
+
+    iret = NF90_OPEN(path,NF90_WRITE,fi) 
+
+    hxd = nc_get_dim_by_name(fi,dset%xname,tv,ds,xid) 
+    IF (hxd) THEN
+      IF(.NOT.compare(dset%x,tv)) THEN ; ret = .false. ; RETURN ; ENDIF
+    ENDIF
+
+    IF (.NOT.hxd) THEN
+      ret = nc_set_dim(fi,dset%xname,dset%x,xid)
+      IF (.NOT.ret) THEN ; iret= NF90_CLOSE(fi) ; RETURN ; ENDIF
+    ENDIF
+    ret = .false.
+    iret = nf90_redef(fi)
+    iret = nf90_def_var(fi, TRIM(dset%dname), nf90_double,(/xid/),vid) 
+    IF (iret /= 0) THEN ; iret = NF90_CLOSE(fi) ; RETURN ; ENDIF
+    iret = nf90_enddef(fi)
+    iret = nf90_put_var(fi,vid,dset%data)
+    ret = (iret == 0)
+    iret=NF90_CLOSE(fi)
+    ret = .true.
+    RETURN
+  END FUNCTION nc_wr_1d
+
+  FUNCTION nc_wr_2d(path,dset) RESULT(ret)
+    CHARACTER(len=*), INTENT(in) :: path !! Path of the netcdf file.
+    TYPE(DSET2D), INTENT(in)     :: dset !! Dataset to write
+    LOGICAL                      :: ret  !! Return status
+    INTEGER                                 :: fi,vi,nd,ds,iret,nferr
+    INTEGER, DIMENSION(:), ALLOCATABLE      :: di
+    REAL(kind=8), DIMENSION(:), ALLOCATABLE :: tv
+    CHARACTER(len=15)                       :: i2s
+    LOGICAL                                 :: hxd,hyd,ok
+    INTEGER                                 :: xid,yid,vid 
+    ret = has_data(dset)
+    IF (.NOT.ret) RETURN
+    ret = .false.
+    INQUIRE(FILE=TRIM(path),EXIST=ok)
+    IF (ok) THEN
+      IF (NF90_OPEN(TRIM(path), NF90_WRITE, fi) /= NF90_NOERR) RETURN
+    ELSE
+      IF (NF90_CREATE(TRIM(path), NF90_NOCLOBBER, fi) /= NF90_NOERR) RETURN
+    ENDIF
+    iret = NF90_CLOSE(fi)
+
+    ! if variable already exist: get out of here !
+    IF (get_nc_info(path,dset%dname,fi,vi,di)) RETURN
+
+    iret = NF90_OPEN(path,NF90_WRITE,fi) 
+
+    hxd = nc_get_dim_by_name(fi,dset%xname,tv,ds,xid) 
+    IF (hxd) THEN
+      IF(.NOT.compare(dset%x,tv)) THEN ; ret = .false. ; RETURN ; ENDIF
+    ENDIF
+    hyd = nc_get_dim_by_name(fi,dset%yname,tv,ds,yid) 
+    IF (hyd) THEN
+      IF(.NOT.compare(dset%y,tv)) THEN ; ret = .false. ; RETURN ; ENDIF
+    ENDIF
+
+    IF (.NOT.hxd) THEN
+      ret = nc_set_dim(fi,dset%xname,dset%x,xid)
+      IF (.NOT.ret) THEN ; iret= NF90_CLOSE(fi) ; RETURN ; ENDIF
+    ENDIF
+    IF (.NOT.hyd) THEN
+      ret = nc_set_dim(fi,dset%yname,dset%y,yid)
+      IF (.NOT.ret) THEN ; iret = NF90_CLOSE(fi) ; RETURN ; ENDIF
+    ENDIF
+    ret = .false.
+    iret = nf90_redef(fi)
+    iret = nf90_def_var(fi, TRIM(dset%dname), nf90_double,(/xid,yid/),vid) 
+    IF (iret /= 0) THEN ; iret = NF90_CLOSE(fi) ; RETURN ; ENDIF
+    iret = nf90_enddef(fi)
+    iret = nf90_put_var(fi,vid,dset%data)
+    ret = (iret == 0)
+    iret=NF90_CLOSE(fi)
+    ret = .true.
+    RETURN
+  END FUNCTION nc_wr_2d
+
+  FUNCTION nc_wr_3d(path,dset) RESULT(ret)
+    CHARACTER(len=*), INTENT(in) :: path !! Path of the netcdf file.
+    TYPE(DSET3D), INTENT(in)     :: dset !! Dataset to write
+    LOGICAL                      :: ret  !! Return status
+    INTEGER                                 :: fi,vi,nd,ds,iret
+    INTEGER, DIMENSION(:), ALLOCATABLE      :: di
+    REAL(kind=8), DIMENSION(:), ALLOCATABLE :: tv
+    CHARACTER(len=15)                       :: i2s
+    LOGICAL                                 :: hxd,hyd,hzd,ok
+    INTEGER                                 :: xid,yid,zid,vid 
+    ret = has_data(dset)
+    IF (.NOT.ret) RETURN
+    ret = .false.
+    INQUIRE(FILE=TRIM(path),EXIST=ok)
+    IF (ok) THEN
+      IF (NF90_OPEN(TRIM(path), NF90_WRITE, fi) /= NF90_NOERR) RETURN
+    ELSE
+      IF (NF90_CREATE(TRIM(path), NF90_NOCLOBBER, fi) /= NF90_NOERR) RETURN
+    ENDIF
+    iret = NF90_CLOSE(fi)
+
+    ! if variable already exist: get out of here !
+    IF (get_nc_info(path,dset%dname,fi,vi,di)) RETURN
+
+    iret = NF90_OPEN(path,NF90_WRITE,fi) 
+
+    hxd = nc_get_dim_by_name(fi,dset%xname,tv,ds,xid) 
+    IF (hxd) THEN
+      IF(.NOT.compare(dset%x,tv)) THEN ; ret = .false. ; RETURN ; ENDIF
+    ENDIF
+    hyd = nc_get_dim_by_name(fi,dset%yname,tv,ds,yid) 
+    IF (hyd) THEN
+      IF(.NOT.compare(dset%y,tv)) THEN ; ret = .false. ; RETURN ; ENDIF
+    ENDIF
+    hzd = nc_get_dim_by_name(fi,dset%zname,tv,ds,zid) 
+    IF (hzd) THEN 
+      IF(.NOT.compare(dset%z,tv)) THEN ; ret = .false. ; RETURN ; ENDIF
+    ENDIF
+
+    IF (.NOT.hxd) THEN
+      ret = nc_set_dim(fi,dset%xname,dset%x,xid)
+      IF (.NOT.ret) THEN ; iret= NF90_CLOSE(fi) ; RETURN ; ENDIF
+    ENDIF
+    IF (.NOT.hyd) THEN
+      ret = nc_set_dim(fi,dset%yname,dset%y,yid)
+      IF (.NOT.ret) THEN ; iret = NF90_CLOSE(fi) ; RETURN ; ENDIF
+    ENDIF
+    IF (.NOT.hzd) THEN
+      ret = nc_set_dim(fi,dset%zname,dset%z,zid)
+      IF (.NOT.ret) THEN ; iret = NF90_CLOSE(fi) ; RETURN ; ENDIF
+    ENDIF
+    ret = .false.
+    iret = nf90_redef(fi)
+    iret = nf90_def_var(fi, TRIM(dset%dname), nf90_double,(/xid,yid,zid/),vid) 
+    IF (iret /= 0) THEN ; iret = NF90_CLOSE(fi) ; RETURN ; ENDIF
+    iret = nf90_enddef(fi)
+    iret = nf90_put_var(fi,vid,dset%data)
+    ret = (iret == 0)
+    iret=NF90_CLOSE(fi)
+    ret = .true.
+    RETURN
+  END FUNCTION nc_wr_3d
+
+  FUNCTION nc_wr_4d(path,dset) RESULT(ret)
+    CHARACTER(len=*), INTENT(in) :: path !! Path of the netcdf file.
+    TYPE(DSET4D), INTENT(in)     :: dset !! Dataset to write
+    LOGICAL                      :: ret  !! Return status
+    INTEGER                                 :: fi,vi,nd,ds,iret
+    INTEGER, DIMENSION(:), ALLOCATABLE      :: di
+    REAL(kind=8), DIMENSION(:), ALLOCATABLE :: tv
+    CHARACTER(len=15)                       :: i2s
+    LOGICAL                                 :: hxd,hyd,hzd,htd,ok
+    INTEGER                                 :: xid,yid,zid,tid,vid 
+    ret = has_data(dset)
+    IF (.NOT.ret) RETURN
+    ret = .false.
+    INQUIRE(FILE=TRIM(path),EXIST=ok)
+    IF (ok) THEN
+      IF (NF90_OPEN(TRIM(path), NF90_WRITE, fi) /= NF90_NOERR) RETURN
+    ELSE
+      IF (NF90_CREATE(TRIM(path), NF90_NOCLOBBER, fi) /= NF90_NOERR) RETURN
+    ENDIF
+    iret = NF90_CLOSE(fi)
+
+    ! if variable already exist: get out of here !
+    IF (get_nc_info(path,dset%dname,fi,vi,di)) RETURN
+
+    iret = NF90_OPEN(path,NF90_WRITE,fi) 
+
+    hxd = nc_get_dim_by_name(fi,dset%xname,tv,ds,xid) 
+    IF (hxd) THEN
+      IF(.NOT.compare(dset%x,tv)) THEN ; ret = .false. ; RETURN ; ENDIF
+    ENDIF
+    hyd = nc_get_dim_by_name(fi,dset%yname,tv,ds,yid) 
+    IF (hyd) THEN
+      IF(.NOT.compare(dset%y,tv)) THEN ; ret = .false. ; RETURN ; ENDIF
+    ENDIF
+    hzd = nc_get_dim_by_name(fi,dset%zname,tv,ds,zid) 
+    IF (hzd) THEN 
+      IF(.NOT.compare(dset%z,tv)) THEN ; ret = .false. ; RETURN ; ENDIF
+    ENDIF
+    htd = nc_get_dim_by_name(fi,dset%tname,tv,ds,tid) 
+    IF (htd) THEN
+      IF(.NOT.compare(dset%t,tv)) THEN ; ret = .false. ; RETURN ; ENDIF
+    ENDIF
+
+    IF (.NOT.hxd) THEN
+      ret = nc_set_dim(fi,dset%xname,dset%x,xid)
+      IF (.NOT.ret) THEN ; iret= NF90_CLOSE(fi) ; RETURN ; ENDIF
+    ENDIF
+    IF (.NOT.hyd) THEN
+      ret = nc_set_dim(fi,dset%yname,dset%y,yid)
+      IF (.NOT.ret) THEN ; iret = NF90_CLOSE(fi) ; RETURN ; ENDIF
+    ENDIF
+    IF (.NOT.hzd) THEN
+      ret = nc_set_dim(fi,dset%zname,dset%z,zid)
+      IF (.NOT.ret) THEN ; iret = NF90_CLOSE(fi) ; RETURN ; ENDIF
+    ENDIF
+    IF (.NOT.htd) THEN
+      ret = nc_set_dim(fi,dset%tname,dset%t,tid)
+      IF (.NOT.ret) THEN ; iret = NF90_CLOSE(fi) ; RETURN ; ENDIF
+    ENDIF
+    ret = .false.
+    iret = nf90_redef(fi)
+    iret = nf90_def_var(fi, TRIM(dset%dname), nf90_double,(/xid,yid,zid,tid/),vid) 
+    IF (iret /= 0) THEN ; iret = NF90_CLOSE(fi) ; RETURN ; ENDIF
+    iret = nf90_enddef(fi)
+    iret = nf90_put_var(fi,vid,dset%data)
+    ret = (iret == 0)
+    iret=NF90_CLOSE(fi)
+    ret = .true.
+    RETURN
+  END FUNCTION nc_wr_4d
+
+  FUNCTION nc_wr_5d(path,dset) RESULT(ret)
+    CHARACTER(len=*), INTENT(in) :: path !! Path of the netcdf file.
+    TYPE(DSET5D), INTENT(in)     :: dset !! Dataset to write
+    LOGICAL                      :: ret  !! Return status
+    INTEGER                                 :: fi,vi,nd,ds,iret
+    INTEGER, DIMENSION(:), ALLOCATABLE      :: di
+    REAL(kind=8), DIMENSION(:), ALLOCATABLE :: tv
+    CHARACTER(len=15)                       :: i2s
+    LOGICAL                                 :: hxd,hyd,hzd,htd,hwd,ok 
+    INTEGER                                 :: xid,yid,zid,tid,wid,vid 
+    ret = has_data(dset)
+    IF (.NOT.ret) RETURN
+    ret = .false.
+    INQUIRE(FILE=TRIM(path),EXIST=ok)
+    IF (ok) THEN
+      IF (NF90_OPEN(TRIM(path), NF90_WRITE, fi) /= NF90_NOERR) RETURN
+    ELSE
+      IF (NF90_CREATE(TRIM(path), NF90_NOCLOBBER, fi) /= NF90_NOERR) RETURN
+    ENDIF
+    iret = NF90_CLOSE(fi)
+
+    ! if variable already exist: get out of here !
+    IF (get_nc_info(path,dset%dname,fi,vi,di)) RETURN
+
+    iret = NF90_OPEN(path,NF90_WRITE,fi) 
+
+    hxd = nc_get_dim_by_name(fi,dset%xname,tv,ds,xid) 
+    IF (hxd) THEN
+      IF(.NOT.compare(dset%x,tv)) THEN ; ret = .false. ; RETURN ; ENDIF
+    ENDIF
+    hyd = nc_get_dim_by_name(fi,dset%yname,tv,ds,yid) 
+    IF (hyd) THEN
+      IF(.NOT.compare(dset%y,tv)) THEN ; ret = .false. ; RETURN ; ENDIF
+    ENDIF
+    hzd = nc_get_dim_by_name(fi,dset%zname,tv,ds,zid) 
+    IF (hzd) THEN 
+      IF(.NOT.compare(dset%z,tv)) THEN ; ret = .false. ; RETURN ; ENDIF
+    ENDIF
+    htd = nc_get_dim_by_name(fi,dset%tname,tv,ds,tid) 
+    IF (htd) THEN
+      IF(.NOT.compare(dset%t,tv)) THEN ; ret = .false. ; RETURN ; ENDIF
+    ENDIF
+    hwd = nc_get_dim_by_name(fi,dset%wname,tv,ds,wid) 
+    IF (hwd) THEN 
+      IF (.NOT.compare(dset%w,tv)) THEN ; ret = .false. ; RETURN ; ENDIF
+    ENDIF
+
+    IF (.NOT.hxd) THEN
+      ret = nc_set_dim(fi,dset%xname,dset%x,xid)
+      IF (.NOT.ret) THEN ; iret= NF90_CLOSE(fi) ; RETURN ; ENDIF
+    ENDIF
+    IF (.NOT.hyd) THEN
+      ret = nc_set_dim(fi,dset%yname,dset%y,yid)
+      IF (.NOT.ret) THEN ; iret = NF90_CLOSE(fi) ; RETURN ; ENDIF
+    ENDIF
+    IF (.NOT.hzd) THEN
+      ret = nc_set_dim(fi,dset%zname,dset%z,zid)
+      IF (.NOT.ret) THEN ; iret = NF90_CLOSE(fi) ; RETURN ; ENDIF
+    ENDIF
+    IF (.NOT.htd) THEN
+      ret = nc_set_dim(fi,dset%tname,dset%t,tid)
+      IF (.NOT.ret) THEN ; iret = NF90_CLOSE(fi) ; RETURN ; ENDIF
+    ENDIF
+    IF (.NOT.hwd) THEN
+      ret = nc_set_dim(fi,dset%wname,dset%w,wid)
+      IF (.NOT.ret) THEN ; iret = NF90_CLOSE(fi) ; RETURN ; ENDIF
+    ENDIF
+    ret = .false.
+    iret = nf90_redef(fi)
+    iret = nf90_def_var(fi, TRIM(dset%dname), nf90_double,(/xid,yid,zid,tid,wid/),vid) 
+    IF (iret /= 0) THEN ; iret = NF90_CLOSE(fi) ; RETURN ; ENDIF
+    iret = nf90_enddef(fi)
+    iret = nf90_put_var(fi,vid,dset%data)
+    ret = (iret == 0)
+    iret=NF90_CLOSE(fi)
+    ret = .true.
+    RETURN
+  END FUNCTION nc_wr_5d
+
+  FUNCTION nc_set_dim(fid,name,values,dimid) RESULT(ret)
+    !! Set a new dimension (and its associated values) in a NetCDF file.
+    INTEGER, INTENT(in)                    :: fid    !! Netcdf file id.
+    CHARACTER(len=*), INTENT(in)           :: name   !! Name of the dimension to set.
+    REAL(kind=8), DIMENSION(:), INTENT(in) :: values !! Associated values.
+    INTEGER, INTENT(out)                   :: dimid  !! Output dimension id.
+    LOGICAL                                :: ret    !! Return status
+    INTEGER :: iret,vid
+    ret = .false.
+    iret = nf90_redef(fid)
+    iret = nf90_def_dim(fid, TRIM(name), size(values), dimid)
+    WRITE(*,'(a)') "nc_set_dim: "//TRIM(name)//" --> "//TRIM(nf90_strerror(iret))
+    IF (iret /=0) RETURN
+    iret = nf90_def_var(fid, TRIM(name), nf90_double,(/dimid/),vid) 
+    IF (iret /=0) RETURN
+    iret = nf90_enddef(fid)
+    iret = nf90_put_var(fid, vid,values)
+    ret = .true.
+  END FUNCTION nc_set_dim
+
+  FUNCTION compare(vec1,vec2,tol) RESULT(ret)
+    !! Compare two vector of double.
+    !!
+    !! The test is checked against the difference (element wise) of the two vector compared to
+    !! to a given tolerance.
+    REAL(kind=8), DIMENSION(:), INTENT(in) :: vec1 !! First vector to compare
+    REAL(kind=8), DIMENSION(:), INTENT(in) :: vec2 !! Second vector to compare
+    REAL(kind=8), INTENT(in), OPTIONAL     :: tol  !! Tolerance to apply for the comparison
+    LOGICAL :: ret                                 !! .true. if both vectors are equal, .false. otherwise.
+    REAL(kind=8) :: ztol
+    INTEGER      :: i
+    ztol = 1d-10 ; IF (PRESENT(tol)) ztol = abs(tol)
+    ret = .false.
+    IF (size(vec1) /= size(vec2)) RETURN
+
+    DO i=1,size(vec1) ; IF (abs(vec1(i)-vec2(i)) > ztol) RETURN ; ENDDO
+    ret = .true.
+  END FUNCTION compare
+
+
+
 END MODULE DATASETS
Index: trunk/LMDZ.TITAN/libf/muphytitan/defined.h
===================================================================
--- trunk/LMDZ.TITAN/libf/muphytitan/defined.h	(revision 1896)
+++ trunk/LMDZ.TITAN/libf/muphytitan/defined.h	(revision 1897)
@@ -1,5 +1,5 @@
-/* Copyright Jérémie Burgalat (2010-2015)
+/* Copyright Jérémie Burgalat (2010-2015,2017)
  * 
- * burgalat.jeremie@gmail.com
+ * jeremie.burgalat@univ-reims.fr
  * 
  * This software is a computer program whose purpose is to provide configuration 
@@ -76,9 +76,9 @@
 /* Defines SSLEN if needed */
 #ifndef SSLEN
-#define SSLEN 200
+#define SSLEN 250
 #endif
 
 /* Defines SLLEN if needed */
 #ifndef SLLEN
-#define SLLEN 2000
+#define SLLEN 2500
 #endif
Index: trunk/LMDZ.TITAN/libf/muphytitan/errors.F90
===================================================================
--- trunk/LMDZ.TITAN/libf/muphytitan/errors.F90	(revision 1896)
+++ trunk/LMDZ.TITAN/libf/muphytitan/errors.F90	(revision 1897)
@@ -1,5 +1,5 @@
-! Copyright Jérémie Burgalat (2010-2015)
-! 
-! burgalat.jeremie@gmail.com
+! Copyright Jérémie Burgalat (2010-2015,2017)
+! 
+! jeremie.burgalat@univ-reims.fr
 ! 
 ! This software is a computer program whose purpose is to provide configuration 
@@ -33,7 +33,7 @@
 
 !! file: errors.F90
-!! summary: Errors handling source file
-!! author: Burgalat
-!! date: 2013-2015
+!! summary: Errors handling source file.
+!! author: J. Burgalat
+!! date: 2013-2015,2017
 
 #include "defined.h"
@@ -84,4 +84,5 @@
     CONTAINS
       PROCEDURE, PUBLIC :: to_string => error_to_string
+        !! Get a string representation of the error
 #endif
   END TYPE error
@@ -201,5 +202,5 @@
     IF (err /= 0) THEN
       WRITE(*,'(a)') error_to_string(err)
-      CALL EXIT(abs(err%id))
+      CALL EXIT(err%id)
     ENDIF
   END SUBROUTINE aborting
@@ -229,5 +230,5 @@
     !! The method raises an assertion and stops the execution if __test__ is .false.
     !! 
-    !! See [[errors(module):assert_r(subroutine)] remark.
+    !! See [[errors(module):assert_r(subroutine)]] remark.
     LOGICAL, INTENT(in)         :: test
      !! Expression to test.
@@ -242,4 +243,29 @@
   END SUBROUTINE assert_w
 
+  FUNCTION free_lun() RESULT(lu)
+    !> Get the first free logical unit
+    !!
+    !! The function loops from 7 to 9999 and returns the first free logical unit.
+    !! @note
+    !! According to Fortran standard, the maximum value for a lun is processor
+    !! dependent. I just assume that [7,9999] is a valid range and I believe that 
+    !! 9992 files to be opened is far enough for any program !
+    !! @note
+    !! If you intend to use loggers object from this library, you should keep in
+    !! mind that loggers open files with the first free logical unit. Consequently
+    !! if you need to perform I/O operations you should use this function to get a
+    !! free lun instead of just randomly set a lun ! 
+    INTEGER :: lu
+      !! First free logical unit in the range [7,9999]  or -1 if no lun is available
+    INTEGER, PARAMETER :: mxlu = 9999
+    LOGICAL :: notfree
+    lu = 6 ; notfree = .true.
+    DO WHILE(notfree.AND.lu<=mxlu)
+      lu=lu+1 ; INQUIRE(unit=lu,OPENED=notfree)
+    ENDDO
+    IF (lu >= mxlu) lu = -1
+  END FUNCTION free_lun
+
+
 
 END MODULE ERRORS
Index: trunk/LMDZ.TITAN/libf/muphytitan/fsystem.F90
===================================================================
--- trunk/LMDZ.TITAN/libf/muphytitan/fsystem.F90	(revision 1896)
+++ trunk/LMDZ.TITAN/libf/muphytitan/fsystem.F90	(revision 1897)
@@ -1,5 +1,5 @@
-! Copyright Jérémie Burgalat (2010-2015)
+! Copyright Jérémie Burgalat (2010-2015,2017)
 ! 
-! burgalat.jeremie@gmail.com
+! jeremie.burgalat@univ-reims.fr
 ! 
 ! This software is a computer program whose purpose is to provide configuration 
@@ -33,7 +33,8 @@
 
 !! file: fsystem.F90
-!! summary: File system methods source file
-!! date: 2013-2015
-!! author: Burgalat
+!! summary: File system methods source file.
+!! author: J. Burgalat
+!! date: 2013-2015,2017
+
 
 #include "defined.h"
@@ -48,10 +49,31 @@
 
   PRIVATE :: get_umask
+  PRIVATE :: c2t
 
   INTEGER, PARAMETER :: MAX_PATH = 512 !! Maximum length of a path
 
-
+  TYPE, PUBLIC :: chrono
+    !! Define a simple chronometer
+    !!
+    !! This object can be used to get an approximation of the execution of some piece of code.
+    REAL(kind=8), PRIVATE    :: cpu_start = 0d0   
+      !! Starting CPU time
+    INTEGER(kind=8), PRIVATE :: clock_start = 0d0 
+      !! Starting clock time
+    LOGICAL, PRIVATE         :: on_run = .false.
+      !! Chrono running state.
+#if HAVE_FTNPROC
+    CONTAINS 
+      PROCEDURE :: is_running => chrono_is_running 
+      PROCEDURE :: start      => chrono_start
+      PROCEDURE :: stop       => chrono_stop
+      PROCEDURE :: reset      => chrono_reset
+      PROCEDURE :: get        => chrono_get
+#endif
+  END TYPE chrono
+
+#ifndef FORD_DOC
+  ! C interfaces
   INTERFACE
-
     FUNCTION strlen_c(s) RESULT(length) bind(C,name="strlen")
       !! Get length of C-string up to (but not including) the terminator
@@ -68,5 +90,5 @@
 
     FUNCTION errno_c() BIND(C,name="c_get_errno")
-      !! Get last error numbero
+      !! Get last error numero
       IMPORT C_INT
       INTEGER(kind=C_INT) :: errno_c !! Last errno
@@ -209,4 +231,12 @@
       INTEGER(kind=C_INT)                    :: mkdirp_c   !! 0 on success, last errno on failure 
     END FUNCTION mkdirp_c
+
+    FUNCTION copy_c(to,from) BIND(C,name="c_copy") 
+      !! Copy a file.
+      IMPORT c_char, C_INT
+      CHARACTER(kind=c_char), INTENT(in)  :: to(*)    !! Destination path.
+      CHARACTER(kind=c_char), INTENT(in)  :: from(*)  !! Input file path to copy.
+      INTEGER(kind=C_INT)                 :: copy_c !! 0 on success, 1 on failure.
+    END FUNCTION copy_c
 
     FUNCTION remove_c(path) BIND(C,name="c_remove") 
@@ -255,6 +285,26 @@
     END FUNCTION termsize_c
 
+    FUNCTION getCurrentRSS_c() BIND(C, name="c_getCurrentRSS")
+      !! Get the current resident set size memory in bytes.
+      IMPORT  C_SIZE_T
+      INTEGER(kind=C_SIZE_T) :: getCurrentRSS_c !! Current resident set size in bytes (0 if not available).
+    END FUNCTION getCurrentRSS_c 
+
+    FUNCTION getPeakRSS_c() BIND(C, name="c_getPeakRSS")
+      !! Get the peak resident set size memory in bytes.
+      IMPORT  C_SIZE_T
+      INTEGER(kind=C_SIZE_T) :: getPeakRSS_c !! Peak resident set size in bytes (0 if not available).
+    END FUNCTION getPeakRSS_c 
+
+    FUNCTION getSystemMemory_c(total,avail,free) BIND(C, name='c_getSystemMemory')
+      !! Get global memory informations.
+      IMPORT C_LONG_LONG,C_INT
+      INTEGER(kind=C_LONG_LONG), INTENT(out) :: total             !! Total available memory.
+      INTEGER(kind=C_LONG_LONG), INTENT(out) :: avail             !! Current available memory.
+      INTEGER(kind=C_LONG_LONG), INTENT(out) :: free              !! Current free memory.
+      INTEGER(kind=C_INT)                    :: getSystemMemory_c !! status, 0 on success, 1 otherwise. 
+    END FUNCTION getSystemMemory_c
   END INTERFACE
-
+#endif
 
   CONTAINS
@@ -291,5 +341,5 @@
     !! @attention
     !! The method does not free the underlying C string and it should be free using 
-    !! [[fsystem(module):free_c(interface)]] method.
+    !! the subroutine free_c(_cstr_).
     TYPE(C_PTR), INTENT(in) :: cstr
       !! A TYPE(C_PTR) that represent the pointer to the C char array.
@@ -476,4 +526,17 @@
     RETURN
   END FUNCTION fs_getcwd
+
+  FUNCTION fs_copy(input,output) RESULT(ret)
+    !! Copy input file into output file.
+    CHARACTER(len=*), INTENT(in)  :: input  !! Input file path to copy.
+    CHARACTER(len=*), INTENT(in)  :: output !! Output file path destination.
+    LOGICAL :: ret                          !! True on success, false otherwise.
+    IF (LEN_TRIM(input) == 0 .OR. LEN_TRIM(output) == 0 .OR. input == output) THEN
+      ret = .false.
+    ELSE
+      ret = INT(copy_c(cstring(ADJUSTL(output)),cstring(ADJUSTL(input)))) == 0
+    ENDIF
+    RETURN
+  END FUNCTION fs_copy
 
   FUNCTION fs_remove(path) RESULT(ret)
@@ -880,4 +943,69 @@
   END SUBROUTINE fs_msleep
 
+  FUNCTION fs_get_memory(peak,units) RESULT(mem)
+    !! Get the memory usage of the current process.
+    LOGICAL, INTENT(in), OPTIONAL          :: peak  !! True to retrieve the peak RSS memory, otherwise retrieve the current RSS memory. Default to False.
+    CHARACTER(len=*), INTENT(in), OPTIONAL :: units !! Output units: either 'B' (Bytes),'KB' (Kilo-),'MB' (Mega-),'GB' (Giga-). Default to 'B'.
+    REAL(kind=8)                           :: mem   !! Memory usage.
+    LOGICAL          :: zpeak
+    CHARACTER(len=2) :: zunits
+    zpeak = .false. ; IF (PRESENT(peak)) zpeak = peak
+    zunits = 'B '   ; IF (PRESENT(units)) zunits = units
+    IF (zunits /= 'B' .AND. zunits /= 'KB' .AND. zunits /= 'MB' .AND. zunits /= 'GB') zunits = 'B '
+    IF (zpeak) THEN
+      mem = REAL(getPeakRSS_c(),kind=8)
+    ELSE
+      mem = REAL(getCurrentRSS_c(),kind=8)
+    ENDIF
+    IF (zunits == 'KB') THEN
+      mem = mem / 1024d0
+    ELSE IF (zunits == 'MB') THEN
+      mem = mem / 1048576d0
+    ELSE IF (zunits == 'GB') THEN
+      mem = mem / 1073741824d0
+    ENDIF
+    RETURN
+  END FUNCTION fs_get_memory
+
+  FUNCTION fs_get_system_memory(total,available,free,units) RESULT(ret)
+    !! Get informations about system memory.
+    !!
+    !! If no informations is available, output arguments are set to 0 and the method returns false.
+    REAL(kind=8), INTENT(out), OPTIONAL    :: total      !! Total available memory.
+    REAL(kind=8), INTENT(out), OPTIONAL    :: available  !! Current available memory.
+    REAL(kind=8), INTENT(out), OPTIONAL    :: free       !! Current free memory.
+    CHARACTER(len=*), INTENT(in), OPTIONAL :: units      !! Output units: either 'B' (Bytes),'KB' (Kilo-),'MB' (Mega-),'GB' (Giga-). Default to 'B'.
+    LOGICAL                                :: ret        !! True on success, false otherwise.
+    LOGICAL          :: zpeak
+    CHARACTER(len=2) :: zunits
+    INTEGER(kind=8)  :: ztot,zava,zfre    
+
+    zunits = 'B '   ; IF (PRESENT(units)) zunits = units
+    IF (zunits /= 'B' .AND. zunits /= 'KB' .AND. zunits /= 'MB' .AND. zunits /= 'GB') zunits = 'B '
+    ret = INT(getSystemMemory_c(ztot,zava,zfre),kind=4) == 0
+    ztot = ztot * 1024 ; zava = zava * 1024 ; zfre = zfre * 1024 
+
+    IF (PRESENT(total))     total     = ztot
+    IF (PRESENT(available)) available = zava
+    IF (PRESENT(free))      free      = zfre
+    IF (.NOT.ret) RETURN
+
+    IF (zunits == 'KB') THEN
+      IF (PRESENT(total))     total     = ztot / 1024d0
+      IF (PRESENT(available)) available = zava / 1024d0
+      IF (PRESENT(free))      free      = zfre / 1024d0
+    ELSE IF (zunits == 'MB') THEN
+      IF (PRESENT(total))     total     = ztot / 1048576d0
+      IF (PRESENT(available)) available = zava / 1048576d0
+      IF (PRESENT(free))      free      = zfre / 1048576d0
+    ELSE IF (zunits == 'GB') THEN
+      IF (PRESENT(total))     total     = ztot / 1073741824d0
+      IF (PRESENT(available)) available = zava / 1073741824d0
+      IF (PRESENT(free))      free      = zfre / 1073741824d0
+    ENDIF
+    RETURN
+  END FUNCTION fs_get_system_memory
+
+
 !===============================================================================
 ! MODULE MISCELLANEOUS METHODS
@@ -1032,3 +1160,102 @@
   END FUNCTION sz2str
 
+  FUNCTION chrono_is_running(this) RESULT (ret)
+    !! Get chrono's state.
+    OBJECT(chrono), INTENT(in) :: this !! Chrono object reference.
+    LOGICAL :: ret                    !! Running state.
+    ret = this%on_run
+    RETURN
+  END FUNCTION chrono_is_running
+
+  SUBROUTINE chrono_start(this)
+    !! Start the chrono. 
+    !!
+    !! @note
+    !! Calling the method multiple times without explicitly stopping the chrono
+    !! [[chrono(type):stop(bound)]] does nothing (except for the first called).
+    OBJECT(chrono), INTENT(inout) :: this  !! Chrono object reference.
+    IF (.NOT.this%on_run) THEN
+      CALL CPU_TIME(this%cpu_start)
+      CALL SYSTEM_CLOCK(this%clock_start)
+    ENDIF
+    this%on_run = .true.
+  END SUBROUTINE chrono_start 
+
+  SUBROUTINE chrono_stop(this)
+    !! Stop the chrono.
+    OBJECT(chrono), INTENT(inout) :: this !! Chrono object reference.
+    REAL(kind=8)    :: ecpu
+    INTEGER(kind=8) :: eclk,nbm,nbr
+    this%on_run = .false.
+  END SUBROUTINE chrono_stop
+
+  SUBROUTINE chrono_reset(this)
+    !! Reset the chrono's internal elapsed times.
+    OBJECT(chrono), INTENT(inout) :: this !! Chrono object reference.
+    CALL CPU_TIME(this%cpu_start)
+    CALL SYSTEM_CLOCK(this%clock_start)
+  END SUBROUTINE chrono_reset
+
+  SUBROUTINE chrono_get(this,cpu,clock,units) 
+    !! Get elapsed time since last call of start or reset methods.
+    !! 
+    !! The method computes the time elapsed in two ways :
+    !!
+    !! - If the [[fsystem(module):chrono(type)]] is not running, the method retruns 0.
+    !! - Otherwise, elapsed time since the last call of 
+    !!   [[chrono(type):start(bound)]] (or [[chrono(type):reset(bound)]]).
+    OBJECT(chrono), INTENT(in)             :: this
+      !! Chrono object reference.
+    REAL(kind=8), INTENT(out), OPTIONAL    :: cpu
+      !! Elapsed cpu time in seconds by default (see units argument).
+    REAL(kind=8), INTENT(out), OPTIONAL    :: clock 
+      !! Elapsed system clock time in seconds by default (see units argument).
+    CHARACTER(len=2), INTENT(in), OPTIONAL :: units
+      !! A two characters wide string with the units to convert in. Units should
+      !! be one of the following : 'ms', 's' (default), 'm', 'h' or 'd'. 
+    CHARACTER(len=2) :: zu
+    REAL(kind=8)     :: cu, fact
+    INTEGER(kind=8)  :: ck, r, m
+    IF (this%on_run) THEN
+      IF (PRESENT(cpu)) THEN
+        CALL CPU_TIME(cu)
+        cpu = (cu - this%cpu_start)
+      ENDIF
+      IF (PRESENT(clock)) THEN
+        CALL SYSTEM_CLOCK(ck,r,m) 
+        clock = c2t(ck,this%clock_start,r,m)
+      ENDIF
+    ELSE
+      IF (PRESENT(cpu))   cpu = 0d0
+      IF (PRESENT(clock)) clock = 0d0
+    ENDIF
+    fact = 1d0
+    zu = 's' 
+    IF (PRESENT(units))  THEN
+      zu = units
+      SELECT CASE(zu)
+        CASE ('d') ; fact = 3600d0*24.
+        CASE ('h') ; fact = 3600d0
+        CASE ('m') ; fact = 60d0
+        CASE ('ms') ; fact = 1d-3
+        CASE DEFAULT ; fact = 1d0
+      END SELECT
+    ENDIF
+    IF (PRESENT(cpu)) cpu = cpu / fact 
+    IF (PRESENT(clock)) clock = clock / fact
+  END SUBROUTINE chrono_get
+
+  FUNCTION c2t(e,i,r,m) RESULT(time)
+    !! Get the real-time between two clock counts from system_clock.
+    INTEGER(kind=8), INTENT(in) :: e !! Final clock count
+    INTEGER(kind=8), INTENT(in) :: i !! Initial clock count 
+    INTEGER(kind=8), INTENT(in) :: r !! Clock count rate
+    INTEGER(kind=8), INTENT(in) :: m !! Maximum Clock count value
+    REAL(kind=8)    :: time          !! Time in seconds
+    INTEGER(kind=8) :: nc
+    nc = e-i ; IF (e < i) nc = nc+m
+    time = REAL(nc,kind=8)/r
+    RETURN
+  END FUNCTION c2t
 END MODULE FSYSTEM
+
Index: trunk/LMDZ.TITAN/libf/muphytitan/mm_clouds.f90
===================================================================
--- trunk/LMDZ.TITAN/libf/muphytitan/mm_clouds.f90	(revision 1896)
+++ trunk/LMDZ.TITAN/libf/muphytitan/mm_clouds.f90	(revision 1897)
@@ -1,3 +1,3 @@
-! Copyright 2013-2015 Université de Reims Champagne-Ardenne 
+! Copyright 2013-2015,2017 Université de Reims Champagne-Ardenne 
 ! Contributor: J. Burgalat (GSMA, URCA)
 ! email of the author : jeremie.burgalat@univ-reims.fr
@@ -35,5 +35,5 @@
 !! summary: Clouds microphysics module
 !! author: J. Burgalat
-!! date: 2013-2015
+!! date: 2013-2015,2017
 
 MODULE MM_CLOUDS
Index: trunk/LMDZ.TITAN/libf/muphytitan/mm_globals.f90
===================================================================
--- trunk/LMDZ.TITAN/libf/muphytitan/mm_globals.f90	(revision 1896)
+++ trunk/LMDZ.TITAN/libf/muphytitan/mm_globals.f90	(revision 1897)
@@ -1,3 +1,3 @@
-! Copyright 2013-2015 Université de Reims Champagne-Ardenne 
+! Copyright 2013-2015,2017 Université de Reims Champagne-Ardenne 
 ! Contributor: J. Burgalat (GSMA, URCA)
 ! email of the author : jeremie.burgalat@univ-reims.fr
@@ -35,5 +35,5 @@
 !! summary: Parameters and global variables module.
 !! author: J. Burgalat
-!! date: 2013-2015
+!! date: 2013-2015,2017
 
 MODULE MM_GLOBALS
@@ -141,5 +141,5 @@
   ! from swift
   USE CFGPARSE
-  USE STRINGS
+  USE STRING_OP
   USE ERRORS
   IMPLICIT NONE
@@ -166,6 +166,6 @@
   PROTECTED :: mm_rcs, mm_rcf, mm_drad, mm_drho
 
-  LOGICAL, SAVE :: mm_debug = .true.  !! Enable QnD debug mode.
-  LOGICAL, SAVE :: mm_log = .false.   !! Enable log mode.
+  LOGICAL, SAVE :: mm_debug = .true.  !! Enable QnD debug mode (can be used for devel).
+  LOGICAL, SAVE :: mm_log = .false.   !! Enable log mode (for configuration only).
 
   LOGICAL, SAVE :: mm_w_haze_prod = .true. !! Enable/Disable haze production.
@@ -177,4 +177,6 @@
 
   LOGICAL, SAVE :: mm_var_prod = .false. !! Time variation of production rate control flag.
+
+  LOGICAL, SAVE :: mm_use_effg = .true. !! Enable/Disable effective G for computations.
 
   !> Enable/Disable __Fiadero__'s correction.
@@ -689,5 +691,5 @@
       ALLOCATE(mm_spcname(mm_nesp),mm_xESPS(mm_nesp))
       DO i=1,mm_nesp
-        mm_spcname(i) = str_to_lower(species(i))
+        mm_spcname(i) = to_lower(species(i))
         IF(.NOT.cfg_has_section(cp,TRIM(mm_spcname(i)))) THEN
           err = error("mm_global_init: Cannot find "//TRIM(mm_spcname(i))//" properties",-1)
@@ -866,5 +868,5 @@
       !mm_spcname(1:mm_nesp) = species(:)
       DO i=1,mm_nesp
-        mm_spcname(i) = str_to_lower(species(i))
+        mm_spcname(i) = to_lower(species(i))
         IF (.NOT.cfg_has_section(spccfg,TRIM(mm_spcname(i)))) THEN
           err = error("mm_global_init: Cannot find "//TRIM(mm_spcname(i))//" properties",-1)
@@ -1214,5 +1216,6 @@
     REAL(kind=mm_wp), INTENT(in) :: z !! Altitude in meters
     REAL(kind=mm_wp) :: effg          !! Effective gravitational acceleration in \(m.s^{-2}\)
-    effg = mm_g0 * (mm_rpla/(mm_rpla+z))**2
+    effg = mm_g0
+    IF (mm_use_effg) effg = effg * (mm_rpla/(mm_rpla+z))**2
     RETURN
   END FUNCTION mm_effg 
Index: trunk/LMDZ.TITAN/libf/muphytitan/mm_haze.f90
===================================================================
--- trunk/LMDZ.TITAN/libf/muphytitan/mm_haze.f90	(revision 1896)
+++ trunk/LMDZ.TITAN/libf/muphytitan/mm_haze.f90	(revision 1897)
@@ -1,3 +1,3 @@
-! Copyright 2013-2015 Université de Reims Champagne-Ardenne 
+! Copyright 2013-2015,2017 Université de Reims Champagne-Ardenne 
 ! Contributor: J. Burgalat (GSMA, URCA)
 ! email of the author : jeremie.burgalat@univ-reims.fr
@@ -35,6 +35,5 @@
 !! summary: Haze microphysics module.
 !! author: J. Burgalat
-!! date: 2013-2015
-
+!! date: 2013-2015,2017
 MODULE MM_HAZE
   !! Haze microphysics module.
Index: trunk/LMDZ.TITAN/libf/muphytitan/mm_interfaces.f90
===================================================================
--- trunk/LMDZ.TITAN/libf/muphytitan/mm_interfaces.f90	(revision 1896)
+++ trunk/LMDZ.TITAN/libf/muphytitan/mm_interfaces.f90	(revision 1897)
@@ -1,3 +1,3 @@
-! Copyright 2013-2015 Université de Reims Champagne-Ardenne 
+! Copyright 2013-2015,2017 Université de Reims Champagne-Ardenne 
 ! Contributor: J. Burgalat (GSMA, URCA)
 ! email of the author : jeremie.burgalat@univ-reims.fr
@@ -35,5 +35,5 @@
 !! summary: Interfaces module for external functions
 !! author: J. Burgalat
-!! date: 2013-2015
+!! date: 2013-2015,2017
 
 MODULE MM_INTERFACES
Index: trunk/LMDZ.TITAN/libf/muphytitan/mm_lib.f90
===================================================================
--- trunk/LMDZ.TITAN/libf/muphytitan/mm_lib.f90	(revision 1896)
+++ trunk/LMDZ.TITAN/libf/muphytitan/mm_lib.f90	(revision 1897)
@@ -1,3 +1,3 @@
-! Copyright 2013-2015 Université de Reims Champagne-Ardenne 
+! Copyright 2013-2015,2017 Université de Reims Champagne-Ardenne 
 ! Contributor: J. Burgalat (GSMA, URCA)
 ! email of the author : jeremie.burgalat@univ-reims.fr
@@ -35,5 +35,5 @@
 !! summary: MP2M library interface module.
 !! author: J. Burgalat
-!! date: 2013-2015
+!! date: 2013-2015,2017
 
 MODULE MM_LIB
Index: trunk/LMDZ.TITAN/libf/muphytitan/mm_methods.f90
===================================================================
--- trunk/LMDZ.TITAN/libf/muphytitan/mm_methods.f90	(revision 1896)
+++ trunk/LMDZ.TITAN/libf/muphytitan/mm_methods.f90	(revision 1897)
@@ -1,3 +1,3 @@
-! Copyright 2013-2015 Université de Reims Champagne-Ardenne 
+! Copyright 2013-2015,2017 Université de Reims Champagne-Ardenne 
 ! Contributor: J. Burgalat (GSMA, URCA)
 ! email of the author : jeremie.burgalat@univ-reims.fr
@@ -35,5 +35,5 @@
 !! summary: Model miscellaneous methods module.
 !! author: J. Burgalat
-!! date: 2013-2015
+!! date: 2013-2015,2017
 
 MODULE MM_METHODS
Index: trunk/LMDZ.TITAN/libf/muphytitan/mm_microphysic.f90
===================================================================
--- trunk/LMDZ.TITAN/libf/muphytitan/mm_microphysic.f90	(revision 1896)
+++ trunk/LMDZ.TITAN/libf/muphytitan/mm_microphysic.f90	(revision 1897)
@@ -1,3 +1,3 @@
-! Copyright 2013-2015 Université de Reims Champagne-Ardenne 
+! Copyright 2013-2015,2017 Université de Reims Champagne-Ardenne 
 ! Contributor: J. Burgalat (GSMA, URCA)
 ! email of the author : jeremie.burgalat@univ-reims.fr
@@ -35,5 +35,5 @@
 !! brief: Microphysic processes interface module.
 !! author: J. Burgalat
-!! date: 2013-2015
+!! date: 2013-2015,2017
 
 MODULE MM_MICROPHYSIC
@@ -111,14 +111,13 @@
       ! add temporary aerosols tendencies (-> m-3)
       dm0a_f = dm0a_f + zdm0a_f  ; dm3a_f = dm3a_f + zdm3a_f
-      ! sanity check for clouds tendencies
-      WHERE (mm_m0ccn+dm0n*mm_dzlev < 0) ; dm0n = -mm_m0ccn/mm_dzlev ; END WHERE
-      WHERE (mm_m3ccn+dm3n*mm_dzlev < 0) ; dm3n = -mm_m3ccn/mm_dzlev ; END WHERE
-
       ! reverse directly clouds tendencies (-> m-2)
       dm0n   = dm0n(mm_nla:1:-1) * mm_dzlev(mm_nla:1:-1)
       dm3n   = dm3n(mm_nla:1:-1) * mm_dzlev(mm_nla:1:-1)
+      ! sanity check for clouds tendencies
+      WHERE (mm_m0ccn+dm0n < 0) ; dm0n = -mm_m0ccn ; END WHERE
+      WHERE (mm_m3ccn+dm3n < 0) ; dm3n = -mm_m3ccn ; END WHERE
       DO i=1,mm_nesp
         dm3i(:,i)  = dm3i(mm_nla:1:-1,i)  * mm_dzlev(mm_nla:1:-1)
-        WHERE (mm_m3ccn+dm3n*mm_dzlev < 0) ; dm3n = -mm_m3ccn/mm_dzlev ; END WHERE
+        WHERE (mm_m3ice+dm3i < 0) ; dm3i = -mm_m3ice ; END WHERE
         dgazs(:,i) = dgazs(mm_nla:1:-1,i) 
         ! no sanity check for gazs, let's prey.
@@ -127,10 +126,4 @@
       dm0n = 0._mm_wp ; dm3n = 0._mm_wp ; dm3i = 0._mm_wp ; dgazs = 0._mm_wp
     ENDIF
-    ! sanity check
-    WHERE (mm_m0aer_s+dm0a_s*mm_dzlev < 0) ; dm0a_s = -mm_m0aer_s/mm_dzlev ; END WHERE
-    WHERE (mm_m3aer_s+dm3a_f*mm_dzlev < 0) ; dm3a_s = -mm_m3aer_s/mm_dzlev ; END WHERE
-    WHERE (mm_m0aer_f+dm0a_f*mm_dzlev < 0) ; dm0a_f = -mm_m0aer_f/mm_dzlev ; END WHERE
-    WHERE (mm_m3aer_f+dm3a_f*mm_dzlev < 0) ; dm3a_f = -mm_m3aer_f/mm_dzlev ; END WHERE
-
     ! multiply by altitude thickness and reverse vectors so they go from ground to top :)
     dm0a_s = dm0a_s(mm_nla:1:-1) * mm_dzlev(mm_nla:1:-1)
@@ -138,4 +131,9 @@
     dm0a_f = dm0a_f(mm_nla:1:-1) * mm_dzlev(mm_nla:1:-1)
     dm3a_f = dm3a_f(mm_nla:1:-1) * mm_dzlev(mm_nla:1:-1)
+    ! sanity check
+    WHERE (mm_m0aer_s+dm0a_s < 0) ; dm0a_s = -mm_m0aer_s ; END WHERE
+    WHERE (mm_m3aer_s+dm3a_f < 0) ; dm3a_s = -mm_m3aer_s ; END WHERE
+    WHERE (mm_m0aer_f+dm0a_f < 0) ; dm0a_f = -mm_m0aer_f ; END WHERE
+    WHERE (mm_m3aer_f+dm3a_f < 0) ; dm3a_f = -mm_m3aer_f ; END WHERE
     
     RETURN
@@ -167,9 +165,4 @@
     ! Calls haze microphysics
     call mm_haze_microphysics(dm0a_s,dm3a_s,dm0a_f,dm3a_f)
-    ! sanity check
-    WHERE (mm_m0aer_s+dm0a_s*mm_dzlev < 0) ; dm0a_s = -mm_m0aer_s/mm_dzlev ; END WHERE
-    WHERE (mm_m3aer_s+dm3a_f*mm_dzlev < 0) ; dm3a_s = -mm_m3aer_s/mm_dzlev ; END WHERE
-    WHERE (mm_m0aer_f+dm0a_f*mm_dzlev < 0) ; dm0a_f = -mm_m0aer_f/mm_dzlev ; END WHERE
-    WHERE (mm_m3aer_f+dm3a_f*mm_dzlev < 0) ; dm3a_f = -mm_m3aer_f/mm_dzlev ; END WHERE
     ! reverse vectors so they go from ground to top :)
     dm0a_s = dm0a_s(mm_nla:1:-1) * mm_dzlev(mm_nla:1:-1) 
@@ -177,4 +170,9 @@
     dm0a_f = dm0a_f(mm_nla:1:-1) * mm_dzlev(mm_nla:1:-1) 
     dm3a_f = dm3a_f(mm_nla:1:-1) * mm_dzlev(mm_nla:1:-1)
+    ! sanity check
+    WHERE (mm_m0aer_s+dm0a_s < 0) ; dm0a_s = -mm_m0aer_s ; END WHERE
+    WHERE (mm_m3aer_s+dm3a_f < 0) ; dm3a_s = -mm_m3aer_s ; END WHERE
+    WHERE (mm_m0aer_f+dm0a_f < 0) ; dm0a_f = -mm_m0aer_f ; END WHERE
+    WHERE (mm_m3aer_f+dm3a_f < 0) ; dm3a_f = -mm_m3aer_f ; END WHERE
     RETURN
   END FUNCTION muphys_nocld
Index: trunk/LMDZ.TITAN/libf/muphytitan/mm_mprec.F90
===================================================================
--- trunk/LMDZ.TITAN/libf/muphytitan/mm_mprec.F90	(revision 1896)
+++ trunk/LMDZ.TITAN/libf/muphytitan/mm_mprec.F90	(revision 1897)
@@ -1,3 +1,3 @@
-! Copyright 2013-2015 Université de Reims Champagne-Ardenne 
+! Copyright 2013-2015,2017 Université de Reims Champagne-Ardenne 
 ! Contributor: J. Burgalat (GSMA, URCA)
 ! email of the author : jeremie.burgalat@univ-reims.fr
@@ -35,5 +35,5 @@
 !! summary: Library floating point precision module.
 !! author: J. Burgalat
-!! date: 2013-2015
+!! date: 2013-2015,2017
 
 #ifdef HAVE_CONFIG_H
Index: trunk/LMDZ.TITAN/libf/muphytitan/mmp_gcm.f90
===================================================================
--- trunk/LMDZ.TITAN/libf/muphytitan/mmp_gcm.f90	(revision 1896)
+++ trunk/LMDZ.TITAN/libf/muphytitan/mmp_gcm.f90	(revision 1897)
@@ -1,4 +1,42 @@
+! Copyright 2017 Université de Reims Champagne-Ardenne 
+! Contributor: J. Burgalat (GSMA, URCA)
+! email of the author : jeremie.burgalat@univ-reims.fr
+! 
+! This software is a computer program whose purpose is to compute
+! microphysics processes using a two-moments scheme.
+! 
+! This library is governed by the CeCILL license under French law and
+! abiding by the rules of distribution of free software.  You can  use, 
+! modify and/ or redistribute the software under the terms of the CeCILL
+! license as circulated by CEA, CNRS and INRIA at the following URL
+! "http://www.cecill.info". 
+! 
+! As a counterpart to the access to the source code and  rights to copy,
+! modify and redistribute granted by the license, users are provided only
+! with a limited warranty  and the software's author,  the holder of the
+! economic rights,  and the successive licensors  have only  limited
+! liability. 
+! 
+! In this respect, the user's attention is drawn to the risks associated
+! with loading,  using,  modifying and/or developing or reproducing the
+! software by the user in light of its specific status of free software,
+! that may mean  that it is complicated to manipulate,  and  that  also
+! therefore means  that it is reserved for developers  and  experienced
+! professionals having in-depth computer knowledge. Users are therefore
+! encouraged to load and test the software's suitability as regards their
+! requirements in conditions enabling the security of their systems and/or 
+! data to be ensured and,  more generally, to use and operate it in the 
+! same conditions as regards security. 
+! 
+! The fact that you are presently reading this means that you have had
+! knowledge of the CeCILL license and that you accept its terms.
+
+!! file: mmp_gcm.f90
+!! summary: YAMMS interfaces for the LMDZ GCM.
+!! author: J. Burgalat
+!! date: 2017
 MODULE MMP_GCM
   !! Interface to YAMMS for the LMDZ GCM.
+  USE MMP_GLOBALS
   USE MM_LIB
   USE CFGPARSE
@@ -6,104 +44,7 @@
   IMPLICIT NONE
 
-  PUBLIC
-
-  !> Alpha function parameters. 
-  !!
-  !! It stores the parameters of the inter-moments relation functions.
-  !! 
-  !! The inter-moments relation function is represented by the sum of exponential
-  !! quadratic expressions:
-  !!
-  !! $$ 
-  !! \displaystyle \alpha(k) = \sum_{i=1}^{n} \exp\left( a_{i}\times k^{2} + 
-  !! b_{i}\times k^{2} +c_{i}\right) 
-  !! $$
-  TYPE, PUBLIC :: aprm
-    !> Quadratic coefficients of the quadratic expressions.
-    REAL(kind=mm_wp), DIMENSION(:), ALLOCATABLE :: a
-    !> Linear coefficients of the quadratic expressions.
-    REAL(kind=mm_wp), DIMENSION(:), ALLOCATABLE :: b
-    !> Free term of the quadratic expressions.
-    REAL(kind=mm_wp), DIMENSION(:), ALLOCATABLE :: c
-  END TYPE
-
-  !> Size distribution parameters derived type.
-  !!
-  !! It stores the parameters of the size distribution law for Titan.
-  !! 
-  !! The size distribution law is represented by the minimization of a sum of
-  !! power law functions:
-  !!
-  !! $$ 
-  !! \displaystyle n\left(r\right) = \frac{A_{0}}{C+\sum_{i=1}^{n} A_{i}\times
-  !!                                    \left(\frac{r}{r_{c}}\right)^{-b_{i}}}
-  !! $$
-  TYPE, PUBLIC :: nprm
-    !> Scaling factor.
-    REAL(kind=mm_wp)                            :: a0
-    !> Characterisitic radius.
-    REAL(kind=mm_wp)                            :: rc
-    !> Additional constant to the sum of power law.
-    REAL(kind=mm_wp)                            :: c
-    !> Scaling factor of each power law.
-    REAL(kind=mm_wp), DIMENSION(:), ALLOCATABLE :: a
-    !> Power of each power law.
-    REAL(kind=mm_wp), DIMENSION(:), ALLOCATABLE :: b
-  END TYPE 
-
-  !> Inter-moment relation set of parameters for the spherical mode.
-  TYPE(aprm), PUBLIC, SAVE :: mmp_asp
-  !> Inter-moment relation set of parameters for the fractal mode.
-  TYPE(aprm), PUBLIC, SAVE :: mmp_afp
-
-  !> Size-distribution law parameters of the spherical mode.
-  TYPE(nprm), PUBLIC, SAVE :: mmp_pns
-  !> Size-distribution law parameters of the fractal mode.
-  TYPE(nprm), PUBLIC, SAVE :: mmp_pnf
-
-  !> Data set for @f$<Q>_{SF}^{M0}@f$.
-  TYPE(dset2d), PUBLIC, SAVE, TARGET             :: mmp_qbsf0
-  !> Extended values of [[mmp_gcm(module):mmp_qbsf0(variable)]] dataset.
-  REAL(kind=mm_wp), PUBLIC, SAVE, DIMENSION(2,2) :: mmp_qbsf0_e
-  !> Data set for @f$<Q>_{SF}^{M3}@f$.
-  TYPE(dset2d), PUBLIC, SAVE, TARGET             :: mmp_qbsf3
-  !> Extended values of [[mmp_gcm(module):mmp_qbsf3(variable)]] dataset.
-  REAL(kind=mm_wp), PUBLIC, SAVE, DIMENSION(2,2) :: mmp_qbsf3_e
-  !> Data set for @f$<Q>_{FF}^{M0}@f$.
-  TYPE(dset2d), PUBLIC, SAVE, TARGET             :: mmp_qbff0
-  !> Extended values of [[mmp_gcm(module):mmp_qbff0(variable)]] dataset.
-  REAL(kind=mm_wp), PUBLIC, SAVE, DIMENSION(2,2) :: mmp_qbff0_e
-  
-  !> Data set for linear interpolation of transfert probability (M0/CO).
-  TYPE(dset1d), PUBLIC, SAVE, TARGET :: mmp_pco0p
-  !> Data set for linear interpolation of transfert probability (M3/CO).
-  TYPE(dset1d), PUBLIC, SAVE, TARGET :: mmp_pco3p
-  !> Data set for linear interpolation of transfert probability (M0/FM).
-  TYPE(dset1d), PUBLIC, SAVE, TARGET :: mmp_pfm0p
-  !> Data set for linear interpolation of transfert probability (M3/FM). 
-  TYPE(dset1d), PUBLIC, SAVE, TARGET :: mmp_pfm3p
-
-  !> \(b_{0}^{t}\) coefficients for Free-molecular regime kernel approximation.
-  REAL(kind=mm_wp), PUBLIC, SAVE, DIMENSION(5) :: mmp_bt0 = (/1._mm_wp,1._mm_wp,1._mm_wp,1._mm_wp,1._mm_wp/)
-  !> \(b_{3}^{t}\) coefficients for Free-molecular regime kernel approximation.
-  REAL(kind=mm_wp), PUBLIC, SAVE, DIMENSION(5) :: mmp_bt3 = (/1._mm_wp,1._mm_wp,1._mm_wp,1._mm_wp,1._mm_wp/)
-
-  !> Spherical probability transfert control flag.
-  LOGICAL, SAVE :: mmp_w_ps2s = .true.
-  !> Aerosol electric charge correction control flag.
-  LOGICAL, SAVE :: mmp_w_qe   = .true.
-
-
   CONTAINS 
     
-  SUBROUTINE abort_program(err)
-    !! Dump error message and abort the program.
-    TYPE(error), INTENT(in) :: err !! Error object.
-    WRITE(stderr,'(a)') "ERROR: "//TRIM(err%msg) 
-    CALL EXIT(err%id)
-  END SUBROUTINE abort_program
-
-
-  SUBROUTINE mmp_initialize(dt,df,rm,rho_aer,p_prod,tx_prod,rc_prod,rplanet,g0, air_rad,air_mmol,clouds,cfgpath)
+  SUBROUTINE mmp_initialize(dt,p_prod,tx_prod,rc_prod,rplanet,g0, air_rad,air_mmol,clouds,cfgpath)
     !! Initialize global parameters of the model.
     !! 
@@ -112,6 +53,5 @@
     !! default values are suitable for production runs.  
     !! @note
-    !! If the method fails to initialize parameters (i.e. returned error is not 0). Then the model
-    !! should probably be aborted as the global variables of the model will not be correctly setup.
+    !! If the subroutine fails to initialize parameters, the run is aborted.
     !!
     !! @warning
@@ -119,17 +59,12 @@
     !! initializes global variable that are not thread private.
     !!
-    !! '''
+    !! ```
     !! !$OMP SINGLE
     !! call mmp_initialize(...)
     !! !$OMP END SINGLE
+    !! ```
     !!
     REAL(kind=mm_wp), INTENT(in)           :: dt
       !! Microphysics timestep in seconds.
-    REAL(kind=mm_wp), INTENT(in)           :: df
-      !! Fractal dimension of fractal aerosol.
-    REAL(kind=mm_wp), INTENT(in)           :: rm
-      !! Monomer radius in meter.
-    REAL(kind=mm_wp), INTENT(in)           :: rho_aer
-      !! Aerosol density in \(kg.m^{-3}\).
     REAL(kind=mm_wp), INTENT(in)           :: p_prod
       !!  Aerosol production pressure level in Pa.
@@ -151,9 +86,9 @@
       !! Internal microphysic configuration file.
 
-    INTEGER          :: coag_choice
-    REAL(kind=mm_wp) :: fiad_max, fiad_min
-    LOGICAL          :: w_h_prod, w_h_sed, w_h_coag, w_c_sed, w_c_nucond, &
-                        no_fiadero, fwsed_m0, fwsed_m3
-    TYPE(error)      :: err
+    INTEGER                                           :: coag_choice
+    REAL(kind=mm_wp)                                  :: fiad_max, fiad_min,df,rm,rho_aer
+    LOGICAL                                           :: w_h_prod, w_h_sed, w_h_coag, w_c_sed, w_c_nucond, &
+                                                         no_fiadero, fwsed_m0, fwsed_m3
+    TYPE(error)                                       :: err
     INTEGER                                           :: i
     TYPE(cfgparser)                                   :: cparser
@@ -186,4 +121,7 @@
    
     ! YAMMS internal parameters:
+    err = mm_check_opt(cfg_get_value(cparser,"rm",rm),rm,50e-9_mm_wp,mm_log)
+    err = mm_check_opt(cfg_get_value(cparser,"df",df),df,2._mm_wp,mm_log)
+    err = mm_check_opt(cfg_get_value(cparser,"rho_aer",rho_aer),rho_aer,1000._mm_wp,mm_log)
     ! the following parameters are primarily used to test and debug YAMMS.
     ! They are set in an optional configuration file and default to suitable values for production runs.
Index: trunk/LMDZ.TITAN/libf/muphytitan/mmp_globals.f90
===================================================================
--- trunk/LMDZ.TITAN/libf/muphytitan/mmp_globals.f90	(revision 1897)
+++ trunk/LMDZ.TITAN/libf/muphytitan/mmp_globals.f90	(revision 1897)
@@ -0,0 +1,105 @@
+MODULE MMP_GLOBALS
+  !! Interface to YAMMS for the LMDZ GCM.
+  USE MM_LIB
+  USE DATASETS
+  IMPLICIT NONE
+
+  PUBLIC
+
+  !> Alpha function parameters. 
+  !!
+  !! It stores the parameters of the inter-moments relation functions.
+  !! 
+  !! The inter-moments relation function is represented by the sum of exponential
+  !! quadratic expressions:
+  !!
+  !! $$ 
+  !! \displaystyle \alpha(k) = \sum_{i=1}^{n} \exp\left( a_{i}\times k^{2} + 
+  !! b_{i}\times k^{2} +c_{i}\right) 
+  !! $$
+  TYPE, PUBLIC :: aprm
+    !> Quadratic coefficients of the quadratic expressions.
+    REAL(kind=mm_wp), DIMENSION(:), ALLOCATABLE :: a
+    !> Linear coefficients of the quadratic expressions.
+    REAL(kind=mm_wp), DIMENSION(:), ALLOCATABLE :: b
+    !> Free term of the quadratic expressions.
+    REAL(kind=mm_wp), DIMENSION(:), ALLOCATABLE :: c
+  END TYPE
+
+  !> Size distribution parameters derived type.
+  !!
+  !! It stores the parameters of the size distribution law for Titan.
+  !! 
+  !! The size distribution law is represented by the minimization of a sum of
+  !! power law functions:
+  !!
+  !! $$ 
+  !! \displaystyle n\left(r\right) = \frac{A_{0}}{C+\sum_{i=1}^{n} A_{i}\times
+  !!                                    \left(\frac{r}{r_{c}}\right)^{-b_{i}}}
+  !! $$
+  TYPE, PUBLIC :: nprm
+    !> Scaling factor.
+    REAL(kind=mm_wp)                            :: a0
+    !> Characterisitic radius.
+    REAL(kind=mm_wp)                            :: rc
+    !> Additional constant to the sum of power law.
+    REAL(kind=mm_wp)                            :: c
+    !> Scaling factor of each power law.
+    REAL(kind=mm_wp), DIMENSION(:), ALLOCATABLE :: a
+    !> Power of each power law.
+    REAL(kind=mm_wp), DIMENSION(:), ALLOCATABLE :: b
+  END TYPE 
+
+  !> Inter-moment relation set of parameters for the spherical mode.
+  TYPE(aprm), PUBLIC, SAVE :: mmp_asp
+  !> Inter-moment relation set of parameters for the fractal mode.
+  TYPE(aprm), PUBLIC, SAVE :: mmp_afp
+
+  !> Size-distribution law parameters of the spherical mode.
+  TYPE(nprm), PUBLIC, SAVE :: mmp_pns
+  !> Size-distribution law parameters of the fractal mode.
+  TYPE(nprm), PUBLIC, SAVE :: mmp_pnf
+
+  !> Data set for @f$<Q>_{SF}^{M0}@f$.
+  TYPE(dset2d), PUBLIC, SAVE, TARGET             :: mmp_qbsf0
+  !> Extended values of [[mmp_gcm(module):mmp_qbsf0(variable)]] dataset.
+  REAL(kind=mm_wp), PUBLIC, SAVE, DIMENSION(2,2) :: mmp_qbsf0_e
+  !> Data set for @f$<Q>_{SF}^{M3}@f$.
+  TYPE(dset2d), PUBLIC, SAVE, TARGET             :: mmp_qbsf3
+  !> Extended values of [[mmp_gcm(module):mmp_qbsf3(variable)]] dataset.
+  REAL(kind=mm_wp), PUBLIC, SAVE, DIMENSION(2,2) :: mmp_qbsf3_e
+  !> Data set for @f$<Q>_{FF}^{M0}@f$.
+  TYPE(dset2d), PUBLIC, SAVE, TARGET             :: mmp_qbff0
+  !> Extended values of [[mmp_gcm(module):mmp_qbff0(variable)]] dataset.
+  REAL(kind=mm_wp), PUBLIC, SAVE, DIMENSION(2,2) :: mmp_qbff0_e
+  
+  !> Data set for linear interpolation of transfert probability (M0/CO).
+  TYPE(dset1d), PUBLIC, SAVE, TARGET :: mmp_pco0p
+  !> Data set for linear interpolation of transfert probability (M3/CO).
+  TYPE(dset1d), PUBLIC, SAVE, TARGET :: mmp_pco3p
+  !> Data set for linear interpolation of transfert probability (M0/FM).
+  TYPE(dset1d), PUBLIC, SAVE, TARGET :: mmp_pfm0p
+  !> Data set for linear interpolation of transfert probability (M3/FM). 
+  TYPE(dset1d), PUBLIC, SAVE, TARGET :: mmp_pfm3p
+
+  !> \(b_{0}^{t}\) coefficients for Free-molecular regime kernel approximation.
+  REAL(kind=mm_wp), PUBLIC, SAVE, DIMENSION(5) :: mmp_bt0 = (/1._mm_wp,1._mm_wp,1._mm_wp,1._mm_wp,1._mm_wp/)
+  !> \(b_{3}^{t}\) coefficients for Free-molecular regime kernel approximation.
+  REAL(kind=mm_wp), PUBLIC, SAVE, DIMENSION(5) :: mmp_bt3 = (/1._mm_wp,1._mm_wp,1._mm_wp,1._mm_wp,1._mm_wp/)
+
+  !> Spherical probability transfert control flag.
+  LOGICAL, SAVE :: mmp_w_ps2s = .true.
+  !> Aerosol electric charge correction control flag.
+  LOGICAL, SAVE :: mmp_w_qe   = .true.
+
+
+  CONTAINS 
+    
+  SUBROUTINE abort_program(err)
+    !! Dump error message and abort the program.
+    TYPE(error), INTENT(in) :: err !! Error object.
+    WRITE(stderr,'(a)') "ERROR: "//TRIM(err%msg) 
+    CALL EXIT(err%id)
+  END SUBROUTINE abort_program
+
+END MODULE MMP_GLOBALS
Index: trunk/LMDZ.TITAN/libf/muphytitan/mmp_moments.f90
===================================================================
--- trunk/LMDZ.TITAN/libf/muphytitan/mmp_moments.f90	(revision 1897)
+++ trunk/LMDZ.TITAN/libf/muphytitan/mmp_moments.f90	(revision 1897)
@@ -0,0 +1,266 @@
+! Copyright 2013-2015,2017 Université de Reims Champagne-Ardenne 
+! Contributor: J. Burgalat (GSMA, URCA)
+! email of the author : jeremie.burgalat@univ-reims.fr
+! 
+! This software is a computer program whose purpose is to compute
+! microphysics processes using a two-moments scheme.
+! 
+! This library is governed by the CeCILL license under French law and
+! abiding by the rules of distribution of free software.  You can  use, 
+! modify and/ or redistribute the software under the terms of the CeCILL
+! license as circulated by CEA, CNRS and INRIA at the following URL
+! "http://www.cecill.info". 
+! 
+! As a counterpart to the access to the source code and  rights to copy,
+! modify and redistribute granted by the license, users are provided only
+! with a limited warranty  and the software's author,  the holder of the
+! economic rights,  and the successive licensors  have only  limited
+! liability. 
+! 
+! In this respect, the user's attention is drawn to the risks associated
+! with loading,  using,  modifying and/or developing or reproducing the
+! software by the user in light of its specific status of free software,
+! that may mean  that it is complicated to manipulate,  and  that  also
+! therefore means  that it is reserved for developers  and  experienced
+! professionals having in-depth computer knowledge. Users are therefore
+! encouraged to load and test the software's suitability as regards their
+! requirements in conditions enabling the security of their systems and/or 
+! data to be ensured and,  more generally, to use and operate it in the 
+! same conditions as regards security. 
+! 
+! The fact that you are presently reading this means that you have had
+! knowledge of the CeCILL license and that you accept its terms.
+
+!! file: mmp_moments.f90
+!! summary: YAMMS/MP2M model external methods
+!! author: J. Burgalat
+!! date: 2013-2015,2017
+!!
+!! This file contains the definitions of all external methods that should be defined
+!! for mp2m library. 
+!! 
+!! All the methods defined here satisify the interfaces defined in __m_interfaces__ module 
+!! of YAMMS library.
+
+PURE FUNCTION mm_alpha_s(k) RESULT (res)
+  !! Inter-moment relation for spherical aerosols size distribution law.
+  !! 
+  !! The method computes the relation between the kth order moment and the 0th 
+  !! order moment of the size-distribution law:
+  !!
+  !! $$ \dfrac{M_{k}}{M_{0}} = r_{C}^{k} \times \alpha(k,a_{1},...a_{n}) $$
+  !!
+  !! Here, alpha is computed as a sum of expenontial functions.
+  USE MMP_GCM, ONLY : mmp_asp, mm_wp
+  IMPLICIT NONE
+  REAL(kind=mm_wp), INTENT(in) :: k !! k Order of the moment.
+  REAL(kind=mm_wp) :: res           !! Alpha value.
+  res = SUM(dexp(mmp_asp%a*k**2+mmp_asp%b*k+mmp_asp%c))
+  RETURN
+END FUNCTION mm_alpha_s 
+
+PURE FUNCTION mm_alpha_f(k) RESULT (res)
+  !! Inter-moment relation for fractal aerosols size distribution law.
+  !!
+  !! [[mm_alpha_f(function)]] performs the same computations as [[mm_alpha_s(function)]]
+  !! using another set of parameters for the exponential functions.
+  USE MMP_GCM, ONLY : mmp_afp, mm_wp
+  IMPLICIT NONE
+  REAL(kind=mm_wp), INTENT(in) :: k !! k Order of the moment.
+  REAL(kind=mm_wp) :: res           !! Alpha value.
+  res = SUM(dexp(mmp_afp%a*k**2+mmp_afp%b*k+mmp_afp%c))
+  RETURN
+END FUNCTION mm_alpha_f
+
+FUNCTION mm_ps2s(rcs,k,flow,t,p) RESULT(res)
+  !! Get the proportion of aerosols that remains in the spherical mode during SS coagulation.
+  !!
+  !! From __k__ and __flow__ values, the method selects one of the four probability datasets datasets
+  !! in [[mmp_globals(module)]] module (for instance [[mmp_globals(module):mmp_pco0p(variable)]])
+  !! and interpolates linearly probability for the given value of __rcs__, __t__ and __p__.
+  !!
+  !! @warning
+  !! Here, the method assumes the datasets define the probability for __spherical__ particles to 
+  !! be transferred in the __fractal__ mode, but returns the proportion of particles that remains
+  !! in the mode (which is expected by mp2m model).
+  !!
+  !! @attention
+  !! If value cannot be interpolated, the method aborts the program. Normally, it cannot happen 
+  !! since we extrapolate the probability for characteristic radius value out of range.
+  !!
+  !! @attention
+  !! Consequently, as the probability can only range from 0 to 1, it is wise to ensure that the 
+  !! look-up table limits this range: To do so, one can just add two values at the start and end 
+  !! of the table with probabilities respectively set to 0 and 1.
+  USE LINTDSET
+  USE LOCATORS
+  USE MMP_GCM, ONLY : mmp_pco0p,mmp_pfm0p,mmp_pco3p,mmp_pfm3p,mmp_w_ps2s,mm_wp
+  IMPLICIT NONE
+  REAL(kind=mm_wp), INTENT(in) :: rcs
+    !! Characteristic radius of the spherical size-distribution (m).
+  INTEGER, INTENT(in)          :: k
+    !! Order of the moment (0 or 3).
+  INTEGER, INTENT(in)          :: flow
+    !! Flow regime indicator (0: Continous, 1: Free-molecular).
+  REAL(kind=mm_wp), INTENT(in) :: t
+    !! Temperature (K).
+  REAL(kind=mm_wp), INTENT(in) :: p
+    !! Pressure level (Pa).
+  REAL(kind=mm_wp) :: res
+    !! Proportion of spherical particles that stay in the spherical mode during SS coagulation.
+  TYPE(dset1d), POINTER :: pp
+  res = 1._mm_wp
+  IF (rcs <= 0.0_mm_wp .OR. .NOT.mmp_w_ps2s) RETURN 
+  SELECT CASE(k+flow)
+    CASE(0)      ; pp => mmp_pco0p ! 0 = 0 + 0 -> M0 / CO
+    CASE(1)      ; pp => mmp_pfm0p ! 1 = 0 + 1 -> M0 / FM
+    CASE(3)      ; pp => mmp_pco3p ! 3 = 3 + 0 -> M3 / CO
+    CASE(4)      ; pp => mmp_pfm3p ! 4 = 3 + 1 -> M3 / FM
+    CASE DEFAULT ; RETURN
+  END SELECT
+  IF (.NOT.hdcd_lint_dset(rcs,pp,locate_reg_ext,res)) THEN 
+    WRITE(*,'(a)') "mm_moments:ps2s_sc: Cannot interpolate transfert probability"
+    call EXIT(10)
+  ELSE
+    ! 05102017: do not care anymore for bad extrapolation: 
+    ! Bound probability value between 0 and 1
+    ! note: The input look-up table still must have strict monotic variation or
+    !       awkward results can be produced.
+    res = MAX(0.0_mm_wp,MIN(res,1.0_mm_wp))
+    ! we have interpolated f = 1-p and we need p !
+    res = 1._mm_wp - res
+  ENDIF
+END FUNCTION mm_ps2s
+
+FUNCTION mm_qmean(rc1,rc2,order,modes,temp,pres) RESULT(res)
+  !! Get the electric correction for coagulation kernel.
+  !!
+  !! The method computes the eletric charging correction to apply to the coagulation
+  !! kernel as a function of the temperature, pressure and the characteristic radius of
+  !! the mode involved in the coagulation.
+  !! 
+  !! Modes are referred by a two letters uppercase string with the combination of:
+  !!
+  !! - S : spherical mode
+  !! - F : fractal mode
+  !! 
+  !! For example, SS means intra-modal coagulation for spherical particles.
+  !!
+  !! Here the electric charging correction is computed using linear interpolation from
+  !! pre-tabulated values.
+  USE LINTDSET
+  USE LOCATORS
+  USE MMP_GCM, ONLY : mmp_w_qe,mmp_qbsf0,mmp_qbsf3,mmp_qbff0, &
+                      mmp_qbsf0_e,mmp_qbsf3_e,mmp_qbff0_e,mm_wp
+  IMPLICIT NONE
+  REAL(kind=mm_wp), INTENT(in)  :: rc1   !! Characteristic radius of the first mode (m).
+  REAL(kind=mm_wp), INTENT(in)  :: rc2   !! Characteristic radius of the the second mode (m).
+  INTEGER, INTENT(in)           :: order !! Moment's order (0 or 3 expected).
+  CHARACTER(len=2), INTENT(in)  :: modes !! Interaction mode (a combination of [S,F]).
+  REAL(kind=mm_wp), INTENT(in)  :: temp  !! Temperature (K).
+  REAL(kind=mm_wp), INTENT(in)  :: pres  !! Pressure level (Pa). 
+  REAL(kind=mm_wp) :: res                !! Electric charging correction.
+  INTEGER       :: chx,np
+  REAL(kind=mm_wp) :: vmin,vmax
+  REAL(kind=mm_wp) :: r_tmp, t_tmp
+  chx = 0 
+  IF (.NOT.mmp_w_qe) THEN
+    res = 1._mm_wp
+    RETURN
+  ENDIF
+
+  IF (SCAN(modes(1:1),"sS") /= 0) chx = chx + 1
+  IF (SCAN(modes(2:2),"sS") /= 0) chx = chx + 1
+  IF (SCAN(modes(1:1),"fF") /= 0) chx = chx + 3
+  IF (SCAN(modes(2:2),"fF") /= 0) chx = chx + 3
+  chx = chx + order
+  SELECT CASE(chx)
+    CASE(2)      ! M0/SS
+      res = 1._mm_wp 
+    CASE(4)      ! M0/SF
+      ! Fix max values of input parameters
+      r_tmp = MAX(MIN(log(rc1),mmp_qbsf0_e(2,2)),mmp_qbsf0_e(2,1))
+      t_tmp = MAX(MIN(temp,mmp_qbsf0_e(1,2)),mmp_qbsf0_e(1,1))
+      ! Interpolates values
+      IF (.NOT.hdcd_lint_dset(t_tmp,r_tmp,mmp_qbsf0,locate_reg,res)) THEN
+        WRITE(*,'(a)') "mm_moments:mm_qmean: Cannot interpolate mean Qelec"
+        call EXIT(10)
+      ENDIF
+    CASE(5)      ! M3/SS
+      res = 1._mm_wp
+    CASE(6)      ! M0/FF
+      r_tmp = MAX(MIN(log(rc1),mmp_qbff0_e(2,2)),mmp_qbff0_e(2,1))
+      t_tmp = MAX(MIN(temp,mmp_qbff0_e(1,2)),mmp_qbff0_e(1,1))
+      IF (.NOT.hdcd_lint_dset(t_tmp,r_tmp,mmp_qbff0,locate_reg,res)) THEN
+        WRITE(*,'(a)') "mm_moments:mm_qmean: Cannot interpolate mean Qelec"
+        call EXIT(10)
+      ENDIF
+    CASE(7)      ! M3/SF
+      r_tmp = MAX(MIN(log(rc1),mmp_qbsf3_e(2,2)),mmp_qbsf3_e(2,1))
+      t_tmp = MAX(MIN(temp,mmp_qbsf3_e(1,2)),mmp_qbsf3_e(1,1))
+      IF (.NOT.hdcd_lint_dset(t_tmp,r_tmp,mmp_qbsf3,locate_reg,res)) THEN
+        WRITE(*,'(a)') "mm_moments:mm_qmean: Cannot interpolate mean Qelec"
+        call EXIT(10)
+      ENDIF
+    CASE DEFAULT ! anything else :)
+      res = 1._mm_wp
+  END SELECT
+  RETURN
+END FUNCTION mm_qmean
+
+PURE FUNCTION mm_get_btk(t,k) RESULT(res)
+  !! Get the \(b_{k}^{T}\) coefficient of the Free Molecular regime.
+  !! 
+  !! The method get the value of the Free-molecular regime coagulation pre-factor \(b_{k}^{T}\).
+  !! For more details about this coefficient, please read [Coagulation](page/haze.html#coagulation) 
+  !! documentation page.
+  !!
+  !! @warning
+  !! __k__ can only be one of the following value : 0 or 3. __t__ ranges only from 1 to 5.
+  USE MMP_GCM, ONLY : mmp_bt0,mmp_bt3,mm_wp
+  IMPLICIT NONE
+  INTEGER, INTENT(in) :: t !! Type index of the \(b_{k}^{T}\) coefficient to get
+  INTEGER, INTENT(in) :: k !! Moment Order of the \(b_{k}^{T}\) coefficient to get
+  REAL(kind=mm_wp) :: res  !! \(b_{k}^{T}\) coefficient
+  IF (.NOT.(k == 3 .OR. k == 0)) res = 0._mm_wp
+  IF (t > 5 .OR. t < 1) res = 0._mm_wp
+  IF (k == 0) THEN
+    res = mmp_bt0(t)
+  ELSE IF (k == 3) THEN
+    res = mmp_bt3(t)
+  ENDIF
+  RETURN
+END FUNCTION mm_get_btk
+
+ELEMENTAL FUNCTION mm_eta_g(t) RESULT (res)
+  !! Get the air viscosity at a given temperature.
+  !!
+  !! The function computes the air viscosity at temperature __t__ using Sutherland method.
+  USE MMP_GCM, ONLY: mm_wp
+  IMPLICIT NONE
+  REAL(kind=mm_wp), INTENT(in) :: t !! Temperature (K).
+  REAL(kind=mm_wp) :: res           !! Air viscosity at given temperature (\(Pa.s^{-1}\)).
+  REAL (kind=mm_wp), PARAMETER :: eta0 = 1.75e-5_mm_wp, &
+                                  tsut = 109._mm_wp,    &
+                                  tref = 293._mm_wp
+  res = eta0 *dsqrt(t/tref)*(1._mm_wp+tsut/tref)/(1._mm_wp+tsut/t)
+  RETURN
+END FUNCTION mm_eta_g
+
+ELEMENTAL FUNCTION mm_lambda_g(t,p) RESULT(res)
+  !! Get the air mean free path at given temperature and pressure.
+  !!
+  !! The method computes the air mean free path:
+  !!
+  !! $$ \lambda_{g} = \dfrac{k_{b}T}{4\sqrt{2}\pi r_{a}^2 P} $$
+  !!
+  !! Where \(\lambda_{g}\), is the air mean free path, \(k_{b}\) the Boltzmann constant, T the
+  !! temperature, P the pressure level and \(r_{a}\) the radius of an _air molecule_.
+  USE MMP_GCM, ONLY: mm_wp,mm_pi,mm_air_rad,mm_kboltz
+  IMPLICIT NONE
+  REAL(kind=mm_wp), INTENT(in) :: t !! Temperature (K).
+  REAL(kind=mm_wp), INTENT(in) :: p !! Pressure level (Pa).
+  REAL(kind=mm_wp) :: res           !! Air mean free path (m).
+  res = mm_kboltz*t/(4._mm_wp*dsqrt(2._mm_wp)*mm_pi*(mm_air_rad**2)*p)
+  RETURN
+END FUNCTION mm_lambda_g
Index: trunk/LMDZ.TITAN/libf/muphytitan/mmp_optics.f90
===================================================================
--- trunk/LMDZ.TITAN/libf/muphytitan/mmp_optics.f90	(revision 1897)
+++ trunk/LMDZ.TITAN/libf/muphytitan/mmp_optics.f90	(revision 1897)
@@ -0,0 +1,383 @@
+! Copyright 2017 Université de Reims Champagne-Ardenne 
+! Contributor: J. Burgalat (GSMA, URCA)
+! email of the author : jeremie.burgalat@univ-reims.fr
+! 
+! This software is a computer program whose purpose is to compute
+! microphysics processes using a two-moments scheme.
+! 
+! This library is governed by the CeCILL license under French law and
+! abiding by the rules of distribution of free software.  You can  use, 
+! modify and/ or redistribute the software under the terms of the CeCILL
+! license as circulated by CEA, CNRS and INRIA at the following URL
+! "http://www.cecill.info". 
+! 
+! As a counterpart to the access to the source code and  rights to copy,
+! modify and redistribute granted by the license, users are provided only
+! with a limited warranty  and the software's author,  the holder of the
+! economic rights,  and the successive licensors  have only  limited
+! liability. 
+! 
+! In this respect, the user's attention is drawn to the risks associated
+! with loading,  using,  modifying and/or developing or reproducing the
+! software by the user in light of its specific status of free software,
+! that may mean  that it is complicated to manipulate,  and  that  also
+! therefore means  that it is reserved for developers  and  experienced
+! professionals having in-depth computer knowledge. Users are therefore
+! encouraged to load and test the software's suitability as regards their
+! requirements in conditions enabling the security of their systems and/or 
+! data to be ensured and,  more generally, to use and operate it in the 
+! same conditions as regards security. 
+! 
+! The fact that you are presently reading this means that you have had
+! knowledge of the CeCILL license and that you accept its terms.
+
+!! file: mmp_optics.f90
+!! summary: Interface for YAMMS aerosols optical properties calculations.
+!! author: J. Burgalat
+!! date: 2017
+MODULE MMP_OPTICS
+  !! Optical properties of spherical/fractal aerosols using moments
+  !!
+  !!
+  !! The module contains an initialization function, [mmp_optics(module):mmp_init_aer_optics(function)],
+  !! that must be called before any calls of the other methods. On failure, it returns .false. and
+  !! consequently, all calls to the other methods will fail !
+  !!
+  !! If openMP is enabled the call to [mmp_optics(module):mmp_init_aer_optics(function)] should be
+  !! done by a single thread.
+  !!
+  !! Then the module provides 4 four public methods to compute optical properties in infrared and
+  !! visible channels as a function of moments of the size-distribution:
+  !! 
+  !! - EXT, the total extinction opacity.
+  !! - SSA, the single scattering albedo.
+  !! - ASF, the asymetry factor.
+  !!
+  !! Fractals and spherical aerosols are calculated sperately, but each EXT, SSA and ASF should be added
+  !! to get the global optical properties.
+  USE MMP_GLOBALS
+  USE DATASETS
+
+  IMPLICIT NONE
+
+  PRIVATE
+  PUBLIC :: mmp_initialize_optics
+  PUBLIC :: mmp_sph_optics_vis,mmp_sph_optics_ir
+  PUBLIC :: mmp_fra_optics_vis,mmp_fra_optics_ir
+
+  ! OPTICAL PROPERTIES !
+
+  !> Extinction opacty table (spherical,IR).
+  REAL(kind=8), DIMENSION(:,:), ALLOCATABLE, SAVE :: ext_s_i
+  !> Single scattering albedo table (spherical,IR).
+  REAL(kind=8), DIMENSION(:,:), ALLOCATABLE, SAVE :: sca_s_i
+  !> Asymetry factor table (spherical,IR).
+  REAL(kind=8), DIMENSION(:,:), ALLOCATABLE, SAVE :: asf_s_i
+  !> Extinction opacty table (fractal,IR).
+  REAL(kind=8), DIMENSION(:,:), ALLOCATABLE, SAVE :: ext_f_i
+  !> Single scattering albedo table (fractal,IR).
+  REAL(kind=8), DIMENSION(:,:), ALLOCATABLE, SAVE :: sca_f_i
+  !> Asymetry factor table (fractal,IR).
+  REAL(kind=8), DIMENSION(:,:), ALLOCATABLE, SAVE :: asf_f_i
+
+  !> Extinction opacty table (spherical,VIS).
+  REAL(kind=8), DIMENSION(:,:), ALLOCATABLE, SAVE :: ext_s_v
+  !> Single scattering albedo table (spherical,VIS).
+  REAL(kind=8), DIMENSION(:,:), ALLOCATABLE, SAVE :: sca_s_v
+  !> Asymetry factor table (spherical,VIS).
+  REAL(kind=8), DIMENSION(:,:), ALLOCATABLE, SAVE :: asf_s_v
+  !> Extinction opacty table (fractal,VIS).
+  REAL(kind=8), DIMENSION(:,:), ALLOCATABLE, SAVE :: ext_f_v
+  !> Single scattering albedo table (fractal,VIS).
+  REAL(kind=8), DIMENSION(:,:), ALLOCATABLE, SAVE :: sca_f_v
+  !> Asymetry factor table (fractal,VIS).
+  REAL(kind=8), DIMENSION(:,:), ALLOCATABLE, SAVE :: asf_f_v
+
+
+  INTEGER, SAVE      :: mmp_nrc    = -1  !! Size of radius grid.
+
+  !> Characteristic radius grid
+  REAL(kind=8), DIMENSION(:), ALLOCATABLE, SAVE :: mmp_rc
+
+  CONTAINS
+
+  SUBROUTINE mmp_initialize_optics(path)
+    !! Initialize optics data for aerosols optical properties computation.
+    !!
+    !! @note
+    !! If the subroutine fails to initialize parameters, the run is aborted.
+    !!
+    !! @warning
+    !! The method assumes YAMMS model has been already intialized correctly !
+    !!
+    !! @warning
+    !! If OpenMP is activated, this subroutine must be called in an $OMP SINGLE statement as it 
+    !! initializes global variables that are not thread private.
+    !!
+    !! '''
+    !! !$OMP SINGLE
+    !! call mmp_initialize(...)
+    !! !$OMP END SINGLE
+    CHARACTER(len=*), INTENT(in) :: path !! Path of NetCDF look-up tables file.
+    LOGICAL :: ret
+    WRITE(*,'(a)') "*** mmp_init_aer_optics speaking ***"
+    WRITE(*,'(a)') "I'm about to initialize look-up tables of aerosols optical properties."
+    WRITE(*,'(a)') "If something's wrong... I will abort the program !"
+    IF (.NOT.mm_ini) THEN
+      call abort_program(error("[mmp_init_aer_optics] Too bad mmp_initialize has not been called yet !",2))
+    ENDIF
+    !  look-up tables
+    ret = read_lookup_tables(path)
+    IF (.NOT.ret) &
+      call abort_program(error("[mmp_init_aer_optics] Failed to retrieve data.",2))
+  END SUBROUTINE mmp_initialize_optics
+
+  FUNCTION mmp_sph_optics_vis(M0,M3,iwn,ext,sca,ssa,asf) RESULT(ret)
+    !! Compute optical properties of the spherical mode in the visible range.
+    REAL(kind=mm_wp), INTENT(in)  :: M0  !! 0th order moment of the spherical mode (m-2).
+    REAL(kind=mm_wp), INTENT(in)  :: M3  !! 3rd order moment of the spherical mode (m3.m-2).
+    INTEGER, INTENT(in)           :: iwn !! Index of the wavenumber to compute
+    REAL(kind=mm_wp), INTENT(out) :: ext !! Extinction opacity.
+    REAL(kind=mm_wp), INTENT(out) :: sca !! Scattering.
+    REAL(kind=mm_wp), INTENT(out) :: ssa !! Single scattering albedo. 
+    REAL(kind=mm_wp), INTENT(out) :: asf !! Asymetry factor.
+    LOGICAL                       :: ret !! true on success, false otherwise.
+    INTEGER          :: i,ridx
+    REAL(kind=mm_wp) :: rc1,rc2,rx,rc
+    ret = .false.
+    IF (mmp_nrc == -1) RETURN
+    ret = .true.
+    rc = mm_get_rcs(M0,M3)
+    ridx = mmp_nrc
+    DO i=1, mmp_nrc
+      IF (rc < mmp_rc(i)) THEN
+        ridx = i-1
+        EXIT
+      ENDIF
+    ENDDO
+    IF (ridx == 0) THEN
+      ! out of range lower bound
+      ext = ext_s_v(1,iwn)
+      sca = sca_s_v(1,iwn)
+      asf = asf_s_v(1,iwn)
+      ssa = sca/ext
+    ELSE IF (ridx == mmp_nrc) THEN
+      ! out of range upper bound
+      ext = ext_s_v(mmp_nrc,iwn)
+      sca = sca_s_v(mmp_nrc,iwn)
+      asf = asf_s_v(mmp_nrc,iwn)
+      ssa = sca/ext
+    ELSE
+      ! in range: interpolate
+      rc1 = mmp_rc(ridx) ; rc2 = mmp_rc(ridx+1)
+      rx = (rc-rc1)/(rc2-rc1)
+      ext = exp(log(ext_s_v(ridx,iwn))*(1d0-rx) + log(ext_s_v(ridx+1,iwn))*rx)
+      sca = exp(log(sca_s_v(ridx,iwn))*(1d0-rx) + log(sca_s_v(ridx+1,iwn))*rx)
+      asf = asf_s_v(ridx,iwn)*(1d0-rx) + asf_s_v(ridx+1,iwn)*rx
+      ssa = sca/ext
+    ENDIF
+    ! scale by M0
+    ext = ext * M0
+    RETURN
+  END FUNCTION mmp_sph_optics_vis
+
+  FUNCTION mmp_sph_optics_ir(M0,M3,iwn,ext,sca,ssa,asf) RESULT(ret)
+    !! Compute optical properties of the spherical mode in the infra-red range.
+    REAL(kind=mm_wp), INTENT(in)  :: M0  !! 0th order moment of the spherical mode (m-2).
+    REAL(kind=mm_wp), INTENT(in)  :: M3  !! 3rd order moment of the spherical mode (m3.m-2).
+    INTEGER, INTENT(in)           :: iwn !! Index of the wavenumber to compute
+    REAL(kind=mm_wp), INTENT(out) :: ext !! Extinction opacity.
+    REAL(kind=mm_wp), INTENT(out) :: sca !! Scattering.
+    REAL(kind=mm_wp), INTENT(out) :: ssa !! Single scattering albedo. 
+    REAL(kind=mm_wp), INTENT(out) :: asf !! Asymetry factor.
+    LOGICAL                       :: ret !! true on success, false otherwise.
+    INTEGER          :: i,ridx
+    REAL(kind=mm_wp) :: rc1,rc2,rx,rc
+    ret = .false.
+    IF (mmp_nrc == -1) RETURN
+    ret = .true.
+    rc = mm_get_rcs(M0,M3)
+    ridx = mmp_nrc
+    DO i=1, mmp_nrc
+      IF (rc < mmp_rc(i)) THEN
+        ridx = i-1
+        EXIT
+      ENDIF
+    ENDDO
+    IF (ridx == 0) THEN
+      ! out of range lower bound
+      ext = ext_s_i(1,iwn)
+      sca = sca_s_i(1,iwn)
+      asf = asf_s_i(1,iwn)
+      ssa = sca/ext
+    ELSE IF (ridx == mmp_nrc) THEN
+      ! out of range upper bound
+      ext = ext_s_i(mmp_nrc,iwn)
+      sca = sca_s_i(mmp_nrc,iwn)
+      asf = asf_s_i(mmp_nrc,iwn)
+      ssa = sca/ext
+    ELSE
+      ! in range: interpolate
+      rc1 = mmp_rc(ridx) ; rc2 = mmp_rc(ridx+1)
+      rx = (rc-rc1)/(rc2-rc1)
+      ext = exp(log(ext_s_i(ridx,iwn))*(1d0-rx) + log(ext_s_i(ridx+1,iwn))*rx)
+      sca = exp(log(sca_s_i(ridx,iwn))*(1d0-rx) + log(sca_s_i(ridx+1,iwn))*rx)
+      asf = asf_s_i(ridx,iwn)*(1d0-rx) + asf_s_i(ridx+1,iwn)*rx
+      ssa = sca/ext
+    ENDIF
+    ! scale by M0
+    ext = ext * M0
+    RETURN
+  END FUNCTION mmp_sph_optics_ir
+
+  FUNCTION mmp_fra_optics_vis(M0,M3,iwn,ext,sca,ssa,asf) RESULT(ret)
+    !! Compute optical properties of the spherical mode in the visible range.
+    REAL(kind=mm_wp), INTENT(in)  :: M0  !! 0th order moment of the fractal mode (m-2).
+    REAL(kind=mm_wp), INTENT(in)  :: M3  !! 3rd order moment of the fractal mode (m3.m-2).
+    INTEGER, INTENT(in)           :: iwn !! Index of the wavenumber to compute.
+    REAL(kind=mm_wp), INTENT(out) :: ext !! Extinction opacity.
+    REAL(kind=mm_wp), INTENT(out) :: sca !! Scattering.
+    REAL(kind=mm_wp), INTENT(out) :: ssa !! Single scattering albedo. 
+    REAL(kind=mm_wp), INTENT(out) :: asf !! Asymetry factor.
+    LOGICAL                       :: ret !! true on success, false otherwise.
+    INTEGER          :: i,ridx
+    REAL(kind=mm_wp) :: rc1,rc2,rx,rc
+    ret = .false.
+    IF (mmp_nrc == -1) RETURN
+    ret = .true.
+    rc = mm_get_rcs(M0,M3)
+    ridx = mmp_nrc
+    DO i=1, mmp_nrc
+      IF (rc < mmp_rc(i)) THEN
+        ridx = i-1
+        EXIT
+      ENDIF
+    ENDDO
+    IF (ridx == 0) THEN
+      ! out of range lower bound
+      ext = ext_f_v(1,iwn)
+      sca = sca_f_v(1,iwn)
+      asf = asf_f_v(1,iwn)
+      ssa = sca/ext
+    ELSE IF (ridx == mmp_nrc) THEN
+      ! out of range upper bound
+      ext = ext_f_v(mmp_nrc,iwn)
+      sca = sca_f_v(mmp_nrc,iwn)
+      asf = asf_f_v(mmp_nrc,iwn)
+      ssa = sca/ext
+    ELSE
+      ! in range: interpolate
+      rc1 = mmp_rc(ridx) ; rc2 = mmp_rc(ridx+1)
+      rx = (rc-rc1)/(rc2-rc1)
+      ext = exp(log(ext_f_v(ridx,iwn))*(1d0-rx) + log(ext_f_v(ridx+1,iwn))*rx)
+      sca = exp(log(sca_f_v(ridx,iwn))*(1d0-rx) + log(sca_f_v(ridx+1,iwn))*rx)
+      asf = asf_f_v(ridx,iwn)*(1d0-rx) + asf_f_v(ridx+1,iwn)*rx
+      ssa = sca/ext
+    ENDIF
+    ! scale by M0
+    ext = ext * M0
+    RETURN
+  END FUNCTION mmp_fra_optics_vis
+
+  FUNCTION mmp_fra_optics_ir(M0,M3,iwn,ext,sca,ssa,asf) RESULT(ret)
+    !! Compute optical properties of the spherical mode in the infra-red range.
+    REAL(kind=mm_wp), INTENT(in)  :: M0  !! 0th order moment of the spherical mode (m-2).
+    REAL(kind=mm_wp), INTENT(in)  :: M3  !! 3rd order moment of the spherical mode (m3.m-2).
+    INTEGER, INTENT(in)           :: iwn !! Index of the wavenumber to compute
+    REAL(kind=mm_wp), INTENT(out) :: ext !! Extinction opacity.
+    REAL(kind=mm_wp), INTENT(out) :: sca !! Scattering.
+    REAL(kind=mm_wp), INTENT(out) :: ssa !! Single scattering albedo. 
+    REAL(kind=mm_wp), INTENT(out) :: asf !! Asymetry factor.
+    LOGICAL                       :: ret !! true on success, false otherwise.
+    INTEGER          :: i,ridx
+    REAL(kind=mm_wp) :: rc1,rc2,rx,rc
+    ret = .false.
+    IF (mmp_nrc == -1) RETURN
+    ret = .true.
+    rc = mm_get_rcs(M0,M3)
+    ridx = mmp_nrc
+    DO i=1, mmp_nrc
+      IF (rc < mmp_rc(i)) THEN
+        ridx = i-1
+        EXIT
+      ENDIF
+    ENDDO
+    IF (ridx == 0) THEN
+      ! out of range lower bound
+      ext = ext_f_i(1,iwn)
+      sca = sca_f_i(1,iwn)
+      asf = asf_f_i(1,iwn)
+      ssa = sca/ext
+    ELSE IF (ridx == mmp_nrc) THEN
+      ! out of range upper bound
+      ext = ext_f_i(mmp_nrc,iwn)
+      sca = sca_f_i(mmp_nrc,iwn)
+      asf = asf_f_i(mmp_nrc,iwn)
+      ssa = sca/ext
+    ELSE
+      ! in range: interpolate
+      rc1 = mmp_rc(ridx) ; rc2 = mmp_rc(ridx+1)
+      rx = (rc-rc1)/(rc2-rc1)
+      ext = exp(log(ext_f_i(ridx,iwn))*(1d0-rx) + log(ext_f_i(ridx+1,iwn))*rx)
+      sca = exp(log(sca_f_i(ridx,iwn))*(1d0-rx) + log(sca_f_i(ridx+1,iwn))*rx)
+      asf = asf_f_i(ridx,iwn)*(1d0-rx) + asf_f_i(ridx+1,iwn)*rx
+      ssa = sca/ext
+    ENDIF
+    ! scale by M0
+    ext = ext * M0
+    RETURN
+  END FUNCTION mmp_fra_optics_ir
+
+  FUNCTION read_lookup_tables(path) RESULT(ret)
+    !! Read look-up tables.
+    CHARACTER(len=*), INTENT(in) :: path !! Path of the look-up tables netcdf file.
+    LOGICAL                      :: ret  !! .true. on success, .false. otherwise.
+    REAL(kind=mm_wp), DIMENSION(:), ALLOCATABLE :: sigmas_vi,sigmas_ir
+
+    TYPE(DSET2D) :: dset
+    ! data(nrc,ni|nv)
+    ! INFRARED
+    ret = read_dset(path,"ext_s_i",dset)
+    IF (.NOT.ret) THEN ; WRITE(*,'(a)') "[read_tables] cannot read 'ext_s_i' table" ; RETURN ; ENDIF 
+    ext_s_i = dset%data ; sigmas_ir = dset%y ; mmp_rc = dset%x
+    ret = read_dset(path,"ext_f_i",dset)
+    IF (.NOT.ret) THEN ; WRITE(*,'(a)') "[read_tables] cannot read 'ext_f_i' table" ; RETURN ; ENDIF 
+    ext_f_i = dset%data 
+    ret = read_dset(path,"sca_s_i",dset)
+    IF (.NOT.ret) THEN ; WRITE(*,'(a)') "[read_tables] cannot read 'sca_s_i' table" ; RETURN ; ENDIF 
+    sca_s_i = dset%data 
+    ret = read_dset(path,"sca_f_i",dset)
+    IF (.NOT.ret) THEN ; WRITE(*,'(a)') "[read_tables] cannot read 'sca_f_i' table" ; RETURN ; ENDIF 
+    sca_f_i = dset%data 
+    ret = read_dset(path,"asf_s_i",dset)
+    IF (.NOT.ret) THEN ; WRITE(*,'(a)') "[read_tables] cannot read 'asf_s_i' table" ; RETURN ; ENDIF 
+    asf_s_i = dset%data 
+    ret = read_dset(path,"asf_f_i",dset)
+    IF (.NOT.ret) THEN ; WRITE(*,'(a)') "[read_tables] cannot read 'asf_f_i' table" ; RETURN ; ENDIF 
+    asf_f_i = dset%data 
+    ! VISIBLE
+    ret = read_dset(path,"ext_s_v",dset)
+    IF (.NOT.ret) THEN ; WRITE(*,'(a)') "[read_tables] cannot read 'ext_s_v' table" ; RETURN ; ENDIF 
+    ext_s_v = dset%data ; sigmas_vi = dset%y
+    ret = read_dset(path,"ext_f_v",dset)
+    IF (.NOT.ret) THEN ; WRITE(*,'(a)') "[read_tables] cannot read 'ext_f_v' table" ; RETURN ; ENDIF 
+    ext_f_v = dset%data 
+    ret = read_dset(path,"sca_s_v",dset)
+    IF (.NOT.ret) THEN ; WRITE(*,'(a)') "[read_tables] cannot read 'sca_s_v' table" ; RETURN ; ENDIF 
+    sca_s_v = dset%data 
+    ret = read_dset(path,"sca_f_v",dset)
+    IF (.NOT.ret) THEN ; WRITE(*,'(a)') "[read_tables] cannot read 'sca_f_v' table" ; RETURN ; ENDIF 
+    sca_f_v = dset%data 
+    ret = read_dset(path,"asf_s_v",dset)
+    IF (.NOT.ret) THEN ; WRITE(*,'(a)') "[read_tables] cannot read 'asf_s_v' table" ; RETURN ; ENDIF 
+    asf_s_v = dset%data 
+    ret = read_dset(path,"asf_f_v",dset)
+    IF (.NOT.ret) THEN ; WRITE(*,'(a)') "[read_tables] cannot read 'asf_f_v' table" ; RETURN ; ENDIF 
+    asf_f_v = dset%data 
+    mmp_nrc = SIZE(mmp_rc)
+    ret = .true.
+    RETURN
+  END FUNCTION read_lookup_tables
+
+END MODULE MMP_OPTICS
+
Index: trunk/LMDZ.TITAN/libf/muphytitan/mp_moments.f90
===================================================================
--- trunk/LMDZ.TITAN/libf/muphytitan/mp_moments.f90	(revision 1896)
+++ 	(revision )
@@ -1,266 +1,0 @@
-! Copyright 2013-2015 Université de Reims Champagne-Ardenne 
-! Contributor: J. Burgalat (GSMA, URCA)
-! email of the author : jeremie.burgalat@univ-reims.fr
-! 
-! This software is a computer program whose purpose is to compute
-! microphysics processes using a two-moments scheme.
-! 
-! This library is governed by the CeCILL license under French law and
-! abiding by the rules of distribution of free software.  You can  use, 
-! modify and/ or redistribute the software under the terms of the CeCILL
-! license as circulated by CEA, CNRS and INRIA at the following URL
-! "http://www.cecill.info". 
-! 
-! As a counterpart to the access to the source code and  rights to copy,
-! modify and redistribute granted by the license, users are provided only
-! with a limited warranty  and the software's author,  the holder of the
-! economic rights,  and the successive licensors  have only  limited
-! liability. 
-! 
-! In this respect, the user's attention is drawn to the risks associated
-! with loading,  using,  modifying and/or developing or reproducing the
-! software by the user in light of its specific status of free software,
-! that may mean  that it is complicated to manipulate,  and  that  also
-! therefore means  that it is reserved for developers  and  experienced
-! professionals having in-depth computer knowledge. Users are therefore
-! encouraged to load and test the software's suitability as regards their
-! requirements in conditions enabling the security of their systems and/or 
-! data to be ensured and,  more generally, to use and operate it in the 
-! same conditions as regards security. 
-! 
-! The fact that you are presently reading this means that you have had
-! knowledge of the CeCILL license and that you accept its terms.
-
-!! file: mmp_moments.f90
-!! summary: YAMMS/MP2M model external methods
-!! author: J. Burgalat
-!! date: 2013-2015
-!!
-!! This file contains the definitions of all external methods that should be defined
-!! for mp2m library. 
-!! 
-!! All the methods defined here satisify the interfaces defined in __m_interfaces__ module 
-!! of YAMMS library.
-
-PURE FUNCTION mm_alpha_s(k) RESULT (res)
-  !! Inter-moment relation for spherical aerosols size distribution law.
-  !! 
-  !! The method computes the relation between the kth order moment and the 0th 
-  !! order moment of the size-distribution law:
-  !!
-  !! $$ \dfrac{M_{k}}{M_{0}} = r_{C}^{k} \times \alpha(k,a_{1},...a_{n}) $$
-  !!
-  !! Here, alpha is computed as a sum of expenontial functions.
-  USE MMP_GCM, ONLY : mmp_asp, mm_wp
-  IMPLICIT NONE
-  REAL(kind=mm_wp), INTENT(in) :: k !! k Order of the moment.
-  REAL(kind=mm_wp) :: res           !! Alpha value.
-  res = SUM(dexp(mmp_asp%a*k**2+mmp_asp%b*k+mmp_asp%c))
-  RETURN
-END FUNCTION mm_alpha_s 
-
-PURE FUNCTION mm_alpha_f(k) RESULT (res)
-  !! Inter-moment relation for fractal aerosols size distribution law.
-  !!
-  !! [[mm_alpha_f(function)]] performs the same computations as [[mm_alpha_s(function)]]
-  !! using another set of parameters for the exponential functions.
-  USE MMP_GCM, ONLY : mmp_afp, mm_wp
-  IMPLICIT NONE
-  REAL(kind=mm_wp), INTENT(in) :: k !! k Order of the moment.
-  REAL(kind=mm_wp) :: res           !! Alpha value.
-  res = SUM(dexp(mmp_afp%a*k**2+mmp_afp%b*k+mmp_afp%c))
-  RETURN
-END FUNCTION mm_alpha_f
-
-FUNCTION mm_ps2s(rcs,k,flow,t,p) RESULT(res)
-  !! Get the proportion of aerosols that remains in the spherical mode during SS coagulation.
-  !!
-  !! From __k__ and __flow__ values, the method selects one of the four probability datasets datasets
-  !! in [[mmp_globals(module)]] module (for instance [[mmp_globals(module):mmp_pco0p(variable)]])
-  !! and interpolates linearly probability for the given value of __rcs__, __t__ and __p__.
-  !!
-  !! @warning
-  !! Here, the method assumes the datasets define the probability for __spherical__ particles to 
-  !! be transferred in the __fractal__ mode, but returns the proportion of particles that remains
-  !! in the mode (which is expected by mp2m model).
-  !!
-  !! @attention
-  !! If value cannot be interpolated, the method aborts the program. Normally, it cannot happen 
-  !! since we extrapolate the probability for characteristic radius value out of range.
-  !!
-  !! @attention
-  !! Consequently, as the probability can only range from 0 to 1, it is wise to ensure that the 
-  !! look-up table limits this range: To do so, one can just add two values at the start and end 
-  !! of the table with probabilities respectively set to 0 and 1.
-  USE LINTDSET
-  USE LOCATORS
-  USE MMP_GCM, ONLY : mmp_pco0p,mmp_pfm0p,mmp_pco3p,mmp_pfm3p,mmp_w_ps2s,mm_wp
-  IMPLICIT NONE
-  REAL(kind=mm_wp), INTENT(in) :: rcs
-    !! Characteristic radius of the spherical size-distribution (m).
-  INTEGER, INTENT(in)          :: k
-    !! Order of the moment (0 or 3).
-  INTEGER, INTENT(in)          :: flow
-    !! Flow regime indicator (0: Continous, 1: Free-molecular).
-  REAL(kind=mm_wp), INTENT(in) :: t
-    !! Temperature (K).
-  REAL(kind=mm_wp), INTENT(in) :: p
-    !! Pressure level (Pa).
-  REAL(kind=mm_wp) :: res
-    !! Proportion of spherical particles that stay in the spherical mode during SS coagulation.
-  TYPE(dset1d), POINTER :: pp
-  res = 1._mm_wp
-  IF (rcs <= 0.0_mm_wp .OR. .NOT.mmp_w_ps2s) RETURN 
-  SELECT CASE(k+flow)
-    CASE(0)      ; pp => mmp_pco0p ! 0 = 0 + 0 -> M0 / CO
-    CASE(1)      ; pp => mmp_pfm0p ! 1 = 0 + 1 -> M0 / FM
-    CASE(3)      ; pp => mmp_pco3p ! 3 = 3 + 0 -> M3 / CO
-    CASE(4)      ; pp => mmp_pfm3p ! 4 = 3 + 1 -> M3 / FM
-    CASE DEFAULT ; RETURN
-  END SELECT
-  IF (.NOT.hdcd_lint_dset(rcs,pp,locate_reg_ext,res)) THEN 
-    WRITE(*,'(a)') "mm_moments:ps2s_sc: Cannot interpolate transfert probability"
-    call EXIT(10)
-  ELSE
-    ! 05102017: do not care anymore for bad extrapolation: 
-    ! Bound probability value between 0 and 1
-    ! note: The input look-up table still must have strict monotic variation or
-    !       awkward results can be produced.
-    res = MAX(0.0_mm_wp,MIN(res,1.0_mm_wp))
-    ! we have interpolated f = 1-p and we need p !
-    res = 1._mm_wp - res
-  ENDIF
-END FUNCTION mm_ps2s
-
-FUNCTION mm_qmean(rc1,rc2,order,modes,temp,pres) RESULT(res)
-  !! Get the electric correction for coagulation kernel.
-  !!
-  !! The method computes the eletric charging correction to apply to the coagulation
-  !! kernel as a function of the temperature, pressure and the characteristic radius of
-  !! the mode involved in the coagulation.
-  !! 
-  !! Modes are referred by a two letters uppercase string with the combination of:
-  !!
-  !! - S : spherical mode
-  !! - F : fractal mode
-  !! 
-  !! For example, SS means intra-modal coagulation for spherical particles.
-  !!
-  !! Here the electric charging correction is computed using linear interpolation from
-  !! pre-tabulated values.
-  USE LINTDSET
-  USE LOCATORS
-  USE MMP_GCM, ONLY : mmp_w_qe,mmp_qbsf0,mmp_qbsf3,mmp_qbff0, &
-                      mmp_qbsf0_e,mmp_qbsf3_e,mmp_qbff0_e,mm_wp
-  IMPLICIT NONE
-  REAL(kind=mm_wp), INTENT(in)  :: rc1   !! Characteristic radius of the first mode (m).
-  REAL(kind=mm_wp), INTENT(in)  :: rc2   !! Characteristic radius of the the second mode (m).
-  INTEGER, INTENT(in)           :: order !! Moment's order (0 or 3 expected).
-  CHARACTER(len=2), INTENT(in)  :: modes !! Interaction mode (a combination of [S,F]).
-  REAL(kind=mm_wp), INTENT(in)  :: temp  !! Temperature (K).
-  REAL(kind=mm_wp), INTENT(in)  :: pres  !! Pressure level (Pa). 
-  REAL(kind=mm_wp) :: res                !! Electric charging correction.
-  INTEGER       :: chx,np
-  REAL(kind=mm_wp) :: vmin,vmax
-  REAL(kind=mm_wp) :: r_tmp, t_tmp
-  chx = 0 
-  IF (.NOT.mmp_w_qe) THEN
-    res = 1._mm_wp
-    RETURN
-  ENDIF
-
-  IF (SCAN(modes(1:1),"sS") /= 0) chx = chx + 1
-  IF (SCAN(modes(2:2),"sS") /= 0) chx = chx + 1
-  IF (SCAN(modes(1:1),"fF") /= 0) chx = chx + 3
-  IF (SCAN(modes(2:2),"fF") /= 0) chx = chx + 3
-  chx = chx + order
-  SELECT CASE(chx)
-    CASE(2)      ! M0/SS
-      res = 1._mm_wp 
-    CASE(4)      ! M0/SF
-      ! Fix max values of input parameters
-      r_tmp = MAX(MIN(log(rc1),mmp_qbsf0_e(2,2)),mmp_qbsf0_e(2,1))
-      t_tmp = MAX(MIN(temp,mmp_qbsf0_e(1,2)),mmp_qbsf0_e(1,1))
-      ! Interpolates values
-      IF (.NOT.hdcd_lint_dset(t_tmp,r_tmp,mmp_qbsf0,locate_reg,res)) THEN
-        WRITE(*,'(a)') "mm_moments:mm_qmean: Cannot interpolate mean Qelec"
-        call EXIT(10)
-      ENDIF
-    CASE(5)      ! M3/SS
-      res = 1._mm_wp
-    CASE(6)      ! M0/FF
-      r_tmp = MAX(MIN(log(rc1),mmp_qbff0_e(2,2)),mmp_qbff0_e(2,1))
-      t_tmp = MAX(MIN(temp,mmp_qbff0_e(1,2)),mmp_qbff0_e(1,1))
-      IF (.NOT.hdcd_lint_dset(t_tmp,r_tmp,mmp_qbff0,locate_reg,res)) THEN
-        WRITE(*,'(a)') "mm_moments:mm_qmean: Cannot interpolate mean Qelec"
-        call EXIT(10)
-      ENDIF
-    CASE(7)      ! M3/SF
-      r_tmp = MAX(MIN(log(rc1),mmp_qbsf3_e(2,2)),mmp_qbsf3_e(2,1))
-      t_tmp = MAX(MIN(temp,mmp_qbsf3_e(1,2)),mmp_qbsf3_e(1,1))
-      IF (.NOT.hdcd_lint_dset(t_tmp,r_tmp,mmp_qbsf3,locate_reg,res)) THEN
-        WRITE(*,'(a)') "mm_moments:mm_qmean: Cannot interpolate mean Qelec"
-        call EXIT(10)
-      ENDIF
-    CASE DEFAULT ! anything else :)
-      res = 1._mm_wp
-  END SELECT
-  RETURN
-END FUNCTION mm_qmean
-
-PURE FUNCTION mm_get_btk(t,k) RESULT(res)
-  !! Get the \(b_{k}^{T}\) coefficient of the Free Molecular regime.
-  !! 
-  !! The method get the value of the Free-molecular regime coagulation pre-factor \(b_{k}^{T}\).
-  !! For more details about this coefficient, please read [Coagulation](page/haze.html#coagulation) 
-  !! documentation page.
-  !!
-  !! @warning
-  !! __k__ can only be one of the following value : 0 or 3. __t__ ranges only from 1 to 5.
-  USE MMP_GCM, ONLY : mmp_bt0,mmp_bt3,mm_wp
-  IMPLICIT NONE
-  INTEGER, INTENT(in) :: t !! Type index of the \(b_{k}^{T}\) coefficient to get
-  INTEGER, INTENT(in) :: k !! Moment Order of the \(b_{k}^{T}\) coefficient to get
-  REAL(kind=mm_wp) :: res  !! \(b_{k}^{T}\) coefficient
-  IF (.NOT.(k == 3 .OR. k == 0)) res = 0._mm_wp
-  IF (t > 5 .OR. t < 1) res = 0._mm_wp
-  IF (k == 0) THEN
-    res = mmp_bt0(t)
-  ELSE IF (k == 3) THEN
-    res = mmp_bt3(t)
-  ENDIF
-  RETURN
-END FUNCTION mm_get_btk
-
-ELEMENTAL FUNCTION mm_eta_g(t) RESULT (res)
-  !! Get the air viscosity at a given temperature.
-  !!
-  !! The function computes the air viscosity at temperature __t__ using Sutherland method.
-  USE MMP_GCM, ONLY: mm_wp
-  IMPLICIT NONE
-  REAL(kind=mm_wp), INTENT(in) :: t !! Temperature (K).
-  REAL(kind=mm_wp) :: res           !! Air viscosity at given temperature (\(Pa.s^{-1}\)).
-  REAL (kind=mm_wp), PARAMETER :: eta0 = 1.75e-5_mm_wp, &
-                                  tsut = 109._mm_wp,    &
-                                  tref = 293._mm_wp
-  res = eta0 *dsqrt(t/tref)*(1._mm_wp+tsut/tref)/(1._mm_wp+tsut/t)
-  RETURN
-END FUNCTION mm_eta_g
-
-ELEMENTAL FUNCTION mm_lambda_g(t,p) RESULT(res)
-  !! Get the air mean free path at given temperature and pressure.
-  !!
-  !! The method computes the air mean free path:
-  !!
-  !! $$ \lambda_{g} = \dfrac{k_{b}T}{4\sqrt{2}\pi r_{a}^2 P} $$
-  !!
-  !! Where \(\lambda_{g}\), is the air mean free path, \(k_{b}\) the Boltzmann constant, T the
-  !! temperature, P the pressure level and \(r_{a}\) the radius of an _air molecule_.
-  USE MMP_GCM, ONLY: mm_wp,mm_pi,mm_air_rad,mm_kboltz
-  IMPLICIT NONE
-  REAL(kind=mm_wp), INTENT(in) :: t !! Temperature (K).
-  REAL(kind=mm_wp), INTENT(in) :: p !! Pressure level (Pa).
-  REAL(kind=mm_wp) :: res           !! Air mean free path (m).
-  res = mm_kboltz*t/(4._mm_wp*dsqrt(2._mm_wp)*mm_pi*(mm_air_rad**2)*p)
-  RETURN
-END FUNCTION mm_lambda_g
Index: trunk/LMDZ.TITAN/libf/muphytitan/string_op.F90
===================================================================
--- trunk/LMDZ.TITAN/libf/muphytitan/string_op.F90	(revision 1897)
+++ trunk/LMDZ.TITAN/libf/muphytitan/string_op.F90	(revision 1897)
@@ -0,0 +1,2547 @@
+! Copyright Jérémie Burgalat (2013-2015,2017)
+! 
+! jeremie.burgalat@univ-reims.fr
+! 
+! This software is a computer program whose purpose is to provide configuration 
+! file and command line arguments parsing features to Fortran programs.
+! 
+! This software is governed by the CeCILL-B license under French law and
+! abiding by the rules of distribution of free software.  You can  use, 
+! modify and/ or redistribute the software under the terms of the CeCILL-B
+! license as circulated by CEA, CNRS and INRIA at the following URL
+! "http://www.cecill.info". 
+! 
+! As a counterpart to the access to the source code and  rights to copy,
+! modify and redistribute granted by the license, users are provided only
+! with a limited warranty  and the software's author,  the holder of the
+! economic rights,  and the successive licensors  have only  limited
+! liability. 
+! 
+! In this respect, the user's attention is drawn to the risks associated
+! with loading,  using,  modifying and/or developing or reproducing the
+! software by the user in light of its specific status of free software,
+! that may mean  that it is complicated to manipulate,  and  that  also
+! therefore means  that it is reserved for developers  and  experienced
+! professionals having in-depth computer knowledge. Users are therefore
+! encouraged to load and test the software's suitability as regards their
+! requirements in conditions enabling the security of their systems and/or 
+! data to be ensured and,  more generally, to use and operate it in the 
+! same conditions as regards security. 
+! 
+! The fact that you are presently reading this means that you have had
+! knowledge of the CeCILL-B license and that you accept its terms.
+
+!! file: strings.F90
+!! summary: Strings manipulation source file
+!! author: J. Burgalat
+!! date: 2013-2015,2017
+
+#include "defined.h"
+
+MODULE STRING_OP
+  !! Fortran strings manipulation module
+  !! 
+  !! This module provides methods and objects to manipulate Fortran (allocatable) strings. It defines
+  !! a doubly linked-list of strings, [[string_op(module):words(type)]] and several methods to format
+  !! strings or convert them in other intrinsic types. 
+  USE ERRORS
+  IMPLICIT NONE
+  
+  PRIVATE
+
+
+
+  PUBLIC ::  str2dble_sc,str2dble_ve,str2real_sc,str2real_ve
+  ! errors module (not used but propagated)
+  PUBLIC :: stdout,stderr,noerror,error, error_to_string,aborting
+  
+  ! misc module methods
+  PUBLIC :: to_string, from_string, string_is, remove_quotes, format_string,     &
+            format_paragraph, strip_newline, tokenize, str_length, endswith, &
+            startswith, to_lower, to_upper, add_csi,      &
+            del_csi, reset_csi, str_remove, str_replace,&
+            fancy
+
+  ! words object related methods
+  PUBLIC :: words_length, words_insert, words_append, words_prepend, words_get, &
+            words_set, words_get_max_width, words_get_total_width, words_pop,   &
+            words_remove, words_next, words_previous, words_reset,              &
+            words_valid, words_current, words_extend, words_reverse,            &
+            words_reversed, words_dump, words_to_string, words_to_vector,       &
+            words_clear
+
+  PRIVATE :: fis_affect_int, fis_affect_bool, fis_affect_real,        &
+             fis_affect_double, fis_affect_cplx, fis_affect_dcplx,    &
+             fis_cat_int, fis_cat_bool, fis_cat_real, fis_cat_double, &
+             fis_cat_cplx, fis_cat_dcplx, fis_cat_int_inv,            &
+             fis_cat_bool_inv, fis_cat_real_inv, fis_cat_double_inv,  &
+             fis_cat_cplx_inv, fis_cat_dcplx_inv
+
+  ! Operators
+  PUBLIC :: ASSIGNMENT(=), OPERATOR(/=), OPERATOR(==)
+
+  INTEGER, PUBLIC, PARAMETER :: st_string  = 1 !! String type ID
+  INTEGER, PUBLIC, PARAMETER :: st_logical = 2 !! Logical type ID
+  INTEGER, PUBLIC, PARAMETER :: st_complex = 3 !! Complex type ID
+  INTEGER, PUBLIC, PARAMETER :: st_integer = 4 !! Integer type ID
+  INTEGER, PUBLIC, PARAMETER :: st_real    = 5 !! Real type ID
+  
+  !> List of types names
+  CHARACTER(len=*), DIMENSION(5), PARAMETER, PUBLIC :: st_type_names = &
+  (/ 'string ', 'logical', 'complex', 'integer', 'real   '/)
+
+  INTEGER, PUBLIC, PARAMETER :: st_slen = SSLEN !! Maximum short string length
+  INTEGER, PUBLIC, PARAMETER :: st_llen = SLLEN !! Maximum long string length
+
+
+  
+  INTEGER, PUBLIC, PARAMETER :: FC_BLACK     = 30 !! Black foreground csi code
+  INTEGER, PUBLIC, PARAMETER :: FC_RED       = 31 !! Red foreground csi code
+  INTEGER, PUBLIC, PARAMETER :: FC_GREEN     = 32 !! Green foreground csi code
+  INTEGER, PUBLIC, PARAMETER :: FC_YELLOW    = 33 !! Yellow foreground csi code
+  INTEGER, PUBLIC, PARAMETER :: FC_BLUE      = 34 !! Blue foreground csi code
+  INTEGER, PUBLIC, PARAMETER :: FC_MAGENTA   = 35 !! Magenta foreground csi code
+  INTEGER, PUBLIC, PARAMETER :: FC_CYAN      = 36 !! Cyan foreground csi code
+  INTEGER, PUBLIC, PARAMETER :: FC_WHITE     = 37 !! White foreground csi code
+  INTEGER, PUBLIC, PARAMETER :: BG_BLACK     = 40 !! Black foreground csi code
+  INTEGER, PUBLIC, PARAMETER :: BG_RED       = 41 !! Black background csi code
+  INTEGER, PUBLIC, PARAMETER :: BG_GREEN     = 42 !! Green background csi code
+  INTEGER, PUBLIC, PARAMETER :: BG_YELLOW    = 43 !! Yellow background csi code
+  INTEGER, PUBLIC, PARAMETER :: BG_BLUE      = 44 !! Blue background csi code
+  INTEGER, PUBLIC, PARAMETER :: BG_MAGENTA   = 45 !! Magenta background csi code
+  INTEGER, PUBLIC, PARAMETER :: BG_CYAN      = 46 !! Cyan background csi code
+  INTEGER, PUBLIC, PARAMETER :: BG_WHITE     = 47 !! White background csi code
+  INTEGER, PUBLIC, PARAMETER :: ST_NORMAL    =  0 !! Normal (regular) attribute
+  INTEGER, PUBLIC, PARAMETER :: ST_BOLD      =  1 !! Bold (brighter) attribute
+  INTEGER, PUBLIC, PARAMETER :: ST_ITALIC    =  3 !! Italic attribute (sometimes reverse video or underline)
+  INTEGER, PUBLIC, PARAMETER :: ST_UNDERLINE =  4 !! Underline attribute
+  INTEGER, PUBLIC, PARAMETER :: ST_BLINK     =  5 !! Slow blink mode
+  !> List of all attributes in a vector
+  INTEGER, PUBLIC, PARAMETER, DIMENSION(21) :: attributes = [FC_BLACK,     &
+                                                             FC_RED,       &
+                                                             FC_GREEN,     & 
+                                                             FC_YELLOW,    &
+                                                             FC_BLUE,      &
+                                                             FC_MAGENTA,   &
+                                                             FC_CYAN,      &
+                                                             FC_WHITE,     &
+                                                             BG_BLACK,     &
+                                                             BG_RED,       &
+                                                             BG_GREEN,     &
+                                                             BG_YELLOW,    &
+                                                             BG_BLUE,      &
+                                                             BG_MAGENTA,   &
+                                                             BG_CYAN,      &
+                                                             BG_WHITE,     & 
+                                                             ST_NORMAL,    &
+                                                             ST_BOLD,      &
+                                                             ST_ITALIC,    &
+                                                             ST_UNDERLINE, &
+                                                             ST_BLINK      &
+                                                            ]     
+  
+  !> Aliases for CSI codes.
+  CHARACTER(len=2), DIMENSION(21), PARAMETER, PUBLIC :: csis =(/ &
+                "fk", "fr", "fg", "fy", "fb", "fm", "fc", "fw", &
+                "bk", "br", "bg", "by", "bb", "bm", "bc", "bw", &
+                 "sn", "sb", "si", "su", "sk"/)
+
+  !> [[words(type)]] object assignement interface
+  INTERFACE ASSIGNMENT(=)
+    MODULE PROCEDURE ws_affect
+  END INTERFACE
+  
+  !> Clear either a scalar or a vector of list of [[words(type)]]
+  !!
+  !! The interface encapsulates words _destructors_, that deallocate memory used 
+  !! by the given list(s) of words. This method should be called anytime words 
+  !! object(s) is no longer used to avoid memory leaks.
+  !! @note
+  !! If the library support Derived type finalization, calling destructor is not
+  !! mandatory.
+  INTERFACE words_clear
+    MODULE PROCEDURE ws_clear_sc, ws_clear_ve
+  END INTERFACE
+
+  !> Extend a given [[words(type)]] object either by another or by a string
+  !!
+  !! The interface encapsulates two subroutines:
+  !!
+  !! - [[ws_extend_ws(subroutine)]](this,other) which extends __this__ by __other__ 
+  !!   (both are words objects).
+  !! - [[ws_extend_str(subroutine)]](this,str,delimiter,merge) which splits __str__
+  !!   according to __delimiter__ (and optionally __merge__) and then extends 
+  !!   __this__ with the resulting tokens.
+  INTERFACE words_extend
+    MODULE PROCEDURE ws_extend_ws,ws_extend_str
+  END INTERFACE
+
+  !> Convert an intrinsic type value to a string
+  !!
+  !! This (very) generic interface provides conversion functions from
+  !! intrinsic types to ALLOCATED string.
+  !!
+  !! ```
+  !! (1)  FUNCTION to_string(value)     RESULT(str)
+  !! (2)  FUNCTION to_string(value,fmt) RESULT(str)
+  !! ```
+  !! Where :
+  !!
+  !! - __value__ is the value to convert
+  !! - __fmt__ is a string the format descriptor of the output string. Surrounding
+  !!   parenthesis can be omitted.
+  !! - __str__ is an allocatable string with the converted value in output, or an empty
+  !!   string if the conversion failed. 
+  INTERFACE to_string
+    MODULE PROCEDURE int2str_as,int2str_fs
+    MODULE PROCEDURE log2str_as,log2str_fs
+    MODULE PROCEDURE real2str_as,real2str_fs
+    MODULE PROCEDURE dble2str_as,dble2str_fs
+    MODULE PROCEDURE cplx2str_as,cplx2str_fs
+    MODULE PROCEDURE dcplx2str_as,dcplx2str_fs
+  END INTERFACE
+  
+  !> Convert a string into an intrisinc type
+  !!
+  !! All methods defined in the interface are functions which take in arguments,
+  !! a string (input) and an output variable with the relevant type (or vectors of both). 
+  !! They always return an error object which is set to -5 error code (i.e. cannot cast value)
+  !! on error, otherwise [[errors(module):noerror(variable)]].
+  INTERFACE from_string
+    MODULE PROCEDURE str2int_sc,str2log_sc,str2real_sc,str2dble_sc,str2cplx_sc
+    MODULE PROCEDURE str2int_ve,str2log_ve,str2real_ve,str2dble_ve,str2cplx_ve
+  END INTERFACE
+
+  !> Compute a fancy string
+  !!
+  !! The generic interface adds CSI codes to the given value and returns a fortran intrinsic string.
+  !!
+  !! This is convinient wrapper to [[string_op(module):to_string(interface)]] and 
+  !! [[string_op(module):add_csi(function)]].
+  !!
+  !! ```fortran
+  !! FUNCTION fancy(value, flags) RESULT(str)
+  !! ```
+  !!
+  !! **value** can be either a INTEGER,  REAL, REAL(kind=8), COMPLEX, COMPLEX(kind=8) or STRING.
+  !!
+  !! **flags is a vector of (string) attributes that can take the values as defined in the second
+  !! column of the [list of csi attributes](|url|/page/swift/p02_strings.html#fancy-strings).
+  INTERFACE fancy
+    MODULE PROCEDURE fancy_fstr, fancy_int, fancy_real, fancy_double, fancy_cplx, fancy_dcplx
+  END INTERFACE
+
+  !> Overloaded string assignment operator interface
+  INTERFACE ASSIGNMENT(=)
+    MODULE PROCEDURE fis_affect_int, fis_affect_bool, fis_affect_real,    &
+                     fis_affect_double, fis_affect_cplx, fis_affect_dcplx
+  END INTERFACE
+
+  !> Overloaded string concatentation operator interface
+  INTERFACE OPERATOR(//)
+    MODULE PROCEDURE fis_cat_int, fis_cat_bool, fis_cat_real, fis_cat_double, &
+                     fis_cat_cplx, fis_cat_dcplx
+    MODULE PROCEDURE fis_cat_int_inv, fis_cat_bool_inv, fis_cat_real_inv,     &
+                     fis_cat_double_inv, fis_cat_cplx_inv, fis_cat_dcplx_inv
+  END INTERFACE
+
+  !> Define a linked word
+  !!
+  !! Linked words are only intended to be used within a words type.
+  !! It's part of the doubly linked list words.
+  TYPE, PUBLIC :: word
+#if HAVE_FTNDTSTR    
+    CHARACTER(len=:), ALLOCATABLE :: value !! Value of the word
+#else    
+    !> Value of the word
+    !!
+    !! @warning 
+    !! It is always limited to [[string_op(module):st_slen(variable)]] characters.
+    CHARACTER(len=st_slen)        :: value = ''
+#endif
+    TYPE(word), PRIVATE, POINTER  :: next => null() !! Next word in the list of words
+    TYPE(word), PRIVATE, POINTER  :: prev => null() !! Previous word in the list of words
+  END TYPE word
+  
+  !> Define a list of words
+  TYPE, PUBLIC :: words
+    INTEGER, PRIVATE             :: nw = 0         !! Number of word in the list
+    TYPE(word), PRIVATE, POINTER :: head => null() !! First word in the list
+    TYPE(word), PRIVATE, POINTER :: tail => null() !! Last word in the list
+    TYPE(word), PRIVATE, POINTER :: iter => null() !! Current word (iterator)
+#if HAVE_FTNPROC 
+    CONTAINS
+    PROCEDURE, PRIVATE :: ws_extend_ws
+    PROCEDURE, PRIVATE :: ws_extend_str
+    PROCEDURE, PUBLIC :: length      => words_length
+      !! Get the number of words in the list
+    PROCEDURE, PUBLIC :: insert      => words_insert
+      !! Insert a word at given index
+    PROCEDURE, PUBLIC :: append      => words_append
+      !! Append a word at the end of the list 
+    PROCEDURE, PUBLIC :: prepend     => words_prepend
+      !! Prepend a word at the beginning of the list 
+    PROCEDURE, PUBLIC :: get         => words_get
+      !! Get the word at given index
+    PROCEDURE, PUBLIC :: set         => words_set
+      !! Set a word at given index
+    PROCEDURE, PUBLIC :: max_width   => words_get_max_width
+      !! Get the width of the biggest word in the list
+    PROCEDURE, PUBLIC :: total_width => words_get_total_width
+      !! Get the total width of the words stored in the list
+    PROCEDURE, PUBLIC :: reverse     => words_reverse
+      !! Reverse the list in place
+    PROCEDURE, PUBLIC :: reversed    => words_reversed
+      !! Get a reversed copy of the list 
+    PROCEDURE, PUBLIC :: dump        => words_dump
+      !! Dump words of the list (on per line)
+    PROCEDURE, PUBLIC :: tostring    => words_to_string
+      !! Convert the list in a single string
+    PROCEDURE, PUBLIC :: to_vector   => words_to_vector
+      !! Convert the list in a vector
+    PROCEDURE, PUBLIC :: pop         => words_pop
+      !! Pop a word from the list and returns it 
+    PROCEDURE, PUBLIC :: remove      => words_remove
+      !! Remove a word from the list
+    PROCEDURE, PUBLIC :: next        => words_next
+      !! Go to the next word in the list
+    PROCEDURE, PUBLIC :: previous    => words_previous
+      !! Go to the previous word in the list
+    PROCEDURE, PUBLIC :: reset       => words_reset
+      !! Reset the list's iterator
+    PROCEDURE, PUBLIC :: valid       => words_valid
+      !! Check if iterator position is valid
+    PROCEDURE, PUBLIC :: current     => words_current
+      !! Get the current word in the list
+    GENERIC, PUBLIC :: extend => ws_extend_ws,ws_extend_str
+      !! Extend a list with either a string or another list of words
+#endif
+  END TYPE words
+  
+  CONTAINS
+  
+  FUNCTION word_length(this) RESULT(lgth)
+    !! Get the trimmed length of the word object
+    TYPE(word), INTENT(in) :: this
+      !! A word object
+    INTEGER :: lgth
+      !! The length of the word's value (without trailing spaces)
+#if HAVE_FTNDTSTR    
+    IF (.NOT.ALLOCATED(this%value)) THEN
+      lgth = 0 ; RETURN
+    ENDIF
+#endif    
+    lgth = LEN_TRIM(this%value)
+    RETURN
+  END FUNCTION word_length
+  
+  SUBROUTINE disconnect_word(this)
+    !! Disconnect a word object
+    !!
+    !! The object is no more connected to its neighbours which are connected together.
+    !! @note 
+    !! After this method is called the object is no longer connected to its parent words 
+    !! object and should be deallocated in order to avoid memory leaks.
+    TYPE(word), INTENT(inout) :: this
+      !! A word object to disconnect
+    TYPE(word), POINTER :: pw,nw
+    nw => this%next ; pw => this%prev
+    IF (ASSOCIATED(nw)) nw%prev => pw
+    IF (ASSOCIATED(pw)) pw%next => nw
+    RETURN
+  END SUBROUTINE disconnect_word
+  
+  SUBROUTINE ws_affect(this,other)
+    !! words object assignment operator subroutine
+    TYPE(words), INTENT(out) :: this
+      !! A words object to be assigned
+    TYPE(words), INTENT(in)  :: other
+      !! A words object to assign
+    TYPE(word), POINTER :: cur
+    CALL ws_clear_sc(this)
+    IF (other%nw == 0) THEN
+      RETURN
+    ELSE
+      cur => other%head
+      DO WHILE(associated(cur))
+#if HAVE_FTNDTSTR      
+        IF (.NOT.ALLOCATED(cur%value)) THEN
+          CALL words_append(this,"")
+        ELSE
+          CALL words_append(this,cur%value)
+        ENDIF
+#else
+        CALL words_append(this,cur%value)
+#endif
+        IF (ASSOCIATED(cur,other%iter)) this%iter => this%tail
+        cur => cur%next
+      ENDDO
+    ENDIF
+    RETURN
+  END SUBROUTINE ws_affect
+  
+  SUBROUTINE ini_word(this,value)
+    !! Initialize the first word of a list of words
+    !!
+    !! This subroutine is not a constructor. It is only intended to set the first word 
+    !! object in a words object.
+    TYPE(words), INTENT(inout)   :: this
+      !! A words object reference
+    CHARACTER(len=*), INTENT(in) :: value
+      !! A string with the word used to initialize the list 
+    ALLOCATE(this%head)
+    this%tail => this%head
+    ASSIGN_DTSTR(value,this%tail%value)
+    this%nw = 1
+    RETURN
+  END SUBROUTINE ini_word
+
+  SUBROUTINE ws_clear_sc(obj)
+    !! Clear a list of words
+    !!
+    !! This subroutine deallocates all memory used by the given words object.
+    !! @warning 
+    !! The subroutine should be called whenever a words is no more used (e.g. at 
+    !! the end of the current scope), otherwise memory leaks could occur.
+    TYPE(words),INTENT(inout), TARGET :: obj
+      !! A words object to clear
+    TYPE(word), POINTER :: cur,next
+    IF (obj%nw == 0) RETURN
+    cur => obj%head 
+    DO WHILE(ASSOCIATED(cur))
+      next => cur%next
+      CALL disconnect_word(cur)
+#if HAVE_FTNDTSTR
+      IF (ALLOCATED(cur%value)) DEALLOCATE(cur%value)
+#endif
+      DEALLOCATE(cur)
+      cur => next
+    ENDDO
+    obj%nw = 0
+    obj%head => null() ; obj%tail => null() ; obj%iter => null()
+  END SUBROUTINE ws_clear_sc
+
+  SUBROUTINE ws_clear_ve(objs)
+    !! Clear a vector of list of words
+    !!
+    !! This subroutine deallocates all memory used by the given vector of words objects.
+    !! @warning 
+    !! The subroutine should be called whenever a words is no more used (e.g. at the end 
+    !! of the current scope), otherwise memory leaks could occur.
+    TYPE(words),INTENT(inout), DIMENSION(:) :: objs
+      !! A vector of words objects to clear
+    TYPE(word), POINTER :: cur,next
+    INTEGER             :: i
+    DO i=1,SIZE(objs)
+      call ws_clear_sc(objs(i))
+    ENDDO
+  END SUBROUTINE ws_clear_ve
+
+  SUBROUTINE ws_extend_ws(this, other)
+    !! Extend a list of words with another one
+    OBJECT(words), INTENT(inout) :: this
+      !! A words object to extend
+    TYPE(words), INTENT(in)     :: other
+      !! A words object to extend with
+    TYPE(word), POINTER :: cw
+    IF (other%nw == 0) RETURN
+    cw => other%head
+    DO WHILE(ASSOCIATED(cw))
+      CALL words_append(this,cw%value) ; cw => cw%next
+    ENDDO
+    RETURN
+  END SUBROUTINE ws_extend_ws
+
+  SUBROUTINE ws_extend_str(this,str,delimiter,merge,protect) 
+    !> Extend a list of word with a given string
+    !! @details The method adds a new list of words to the current list by 
+    !! splitting a string using a set of delimiters.
+    !! 
+    !!   - If __delimiter__ is not given, THEN blank space is used.
+    !!   - __delimiter__ can be a string of any length, but each character of 
+    !!     the sequence is seen as a single delimiter. Each time one of these 
+    !!     special character is seen on the string, it is splitted.
+    !!   - If __protect__ is set to .true. THEN delimiter enclosed by
+    !!     either single or double quotes are protected.
+    !!   - The optional argument __merge__ instructs the method wether to merge
+    !!     or not successive delimiters in the string.
+    !! 
+    !! For example, considering the following string:
+    !! <center>@verbatim "I like coffee and bananas." @endverbatim</center>
+    !!   - Used with only __delimiter__ = " e", the method returns the list:
+    !!     <center>"I","lik","","coff","","","and","bananas"</center>
+    !!   - Used with both __delimiter__ = " e" and __merge__ = .true. :
+    !!     <center>"I","lik","coff","and","bananas"</center>
+    !! @warning
+    !! The method does not trim or adjust the input string. Consequently, it can
+    !! add several empty words at the end of the list if the string is not well
+    !! defined.
+    !! @warning To avoid such problems, consider using TRIM() and ADJUSTL()
+    !! function on __str__ actual argument when calling this subroutine.
+    OBJECT(words), INTENT(inout), TARGET   :: this
+      !! A words object to extend
+    CHARACTER(len=*), INTENT(in)           :: str
+      !! A string to split in words
+    CHARACTER(len=*), INTENT(in), OPTIONAL :: delimiter
+      !! An optional string with the words delimiters (default to blank space). 
+    LOGICAL, INTENT(in), OPTIONAL          :: merge
+      !! An optional boolean control flag that instructs the method
+      !! wether to merge or not successive delimiters (default to .false.)
+    LOGICAL, INTENT(in), OPTIONAL          :: protect
+      !! An optional boolean flag with .true. to indicate that 
+      !! delimiter characters between quotes are protected
+    ! - LOCAL
+    INTEGER                       :: sl,p,i,j,stat
+    LOGICAL                       :: zmerge,zprotect,indq,insq,outer
+    CHARACTER(len=:), ALLOCATABLE :: seps
+    CHARACTER(len=:), ALLOCATABLE :: curw
+    CHARACTER(len=1), PARAMETER   :: sq = CHAR(39) ! single quote ascii code
+    CHARACTER(len=1), PARAMETER   :: dq = CHAR(34) ! double quotes ascii code
+    stat=0 ; p=1 ; indq = .false. ; insq = .false. 
+    seps = ' '
+    zmerge = .false. ; IF (PRESENT(merge)) zmerge = merge
+    zprotect = .true. ; IF (PRESENT(protect)) zprotect = protect
+    IF (PRESENT(delimiter)) THEN
+      IF (LEN(delimiter) > 0) seps = delimiter
+    ENDIF
+    sl = LEN(str) ; IF (sl == 0) RETURN
+    outer =     (INDEX(str,sq) == 1 .AND. INDEX(str,sq,.true.) == LEN(str)) & 
+            .OR.(INDEX(str,dq) == 1 .AND. INDEX(str,dq,.true.) == LEN(str))
+    ! no delimiter found or (have outer quotes and should protect)
+    IF (SCAN(str,seps) == 0.OR.(outer.AND.zprotect)) THEN
+      CALL words_append(this,remove_quotes(str)) 
+      RETURN
+    ENDIF
+    ! We have to loop... 
+    i = 1 ; curw=''
+    DO
+      IF (i > sl) EXIT
+      p = SCAN(str(i:),seps) ! position du delimiteur
+      IF (p == 0) THEN
+        ! a gerer
+        curw = curw//TRIM(str(i:))
+        CALL words_append(this,TRIM(str(i:))) ; EXIT
+        curw=''
+      ELSE
+        IF (zprotect) THEN
+          j=i
+          ! starting state
+          DO WHILE(j<i+p)
+            IF (str(j:j) == sq.AND. .NOT.indq) insq = .NOT.insq
+            IF (str(j:j) == dq.AND. .NOT.insq) indq = .NOT.indq
+            j = j+1
+          ENDDO
+          IF ((insq.AND.INDEX(str(j:),"'")/=0) .OR. &
+              (indq.AND.INDEX(str(j:),'"')/=0)) THEN
+            curw=curw//str(i:i+p-1)
+            i=i+p ; CYCLE
+          ENDIF
+        ENDIF
+        IF (p == 1) THEN
+          IF (.NOT.zmerge) THEN
+            curw=''
+            CALL words_append(this,curw)
+          ENDIF
+          i = i + 1 ; CYCLE
+        ELSE
+          curw=curw//str(i:i+p-2)
+          CALL words_append(this,curw)
+          curw = ''
+          i = i + p
+        ENDIF
+      ENDIF
+    ENDDO
+    DEALLOCATE(curw) ; DEALLOCATE(seps)
+    !IF (zprotect) THEN
+    !  ! catching unbalanced quotes
+    !  IF (insq .OR. indq) &
+    !  WRITE(*,'(a)') "extends:warning: unbalanced quotes"
+    !ENDIF
+    RETURN
+  END SUBROUTINE ws_extend_str
+
+  FUNCTION ws_get_ptr(this,idx) RESULT(pted)
+    !! Get the pointer of the word object at given index
+    !!
+    !! The method returns the pointer of the word object at the given index. 
+    !! If index is out of range a null poitner is returned.
+    OBJECT(words), INTENT(in) :: this
+      !! A words object 
+    INTEGER, INTENT(in)       :: idx
+      !! An integer with the index of the desired object in __this__
+    TYPE(word), POINTER :: pted
+      !! A pointer to the selected word object.
+    INTEGER :: i
+    pted => null()
+    IF (idx < 1 .OR. idx > words_length(this)) THEN
+      RETURN
+    ENDIF
+    IF (idx > (this%nw+1)/2) THEN
+      pted => this%tail
+      DO i=1,this%nw - idx ; pted => pted%prev ; ENDDO
+    ELSE
+      pted => this%head
+      DO i=1,idx-1 ; pted => pted%next ; ENDDO
+    ENDIF
+    RETURN
+  END FUNCTION ws_get_ptr
+
+  FUNCTION words_length(this) RESULT(res)
+    !! Get the size of the words object.
+    !!
+    !! The method returns the number of words stored in the given list of words.
+    OBJECT(words), INTENT(in) :: this !! A words object.
+    INTEGER :: res                    !! The number of words in the object.
+    res = this%nw
+    RETURN
+  END FUNCTION words_length
+
+  SUBROUTINE words_insert(this, idx, value)
+    !! Insert a word before given index in a list of words.
+    !!
+    !! The method inserts a new word before the given index in the list of words. If the given index is out
+    !! of range, the method prepends/appends the object based on the index value.
+    OBJECT(words), INTENT(inout)  :: this
+      !! A words object.
+    INTEGER, INTENT(in)          :: idx
+      !! An integer with the index of an object in the list. The new object will be inserted before that index.
+    CHARACTER(len=*), INTENT(in) :: value
+      !! A string with the word to insert in the list.
+    TYPE(word), POINTER :: welt,nx,pv
+    INTEGER             :: i
+    welt => null() ; nx => null() ; pv => null()
+    IF (this%nw == 0) THEN
+      CALL ini_word(this,value)
+    ELSE IF (idx > this%nw) THEN
+      this%nw = this%nw + 1
+      welt => this%tail
+      allocate(this%tail)
+      ASSIGN_DTSTR(value,this%tail%value)
+      this%tail%prev => welt
+      this%tail%prev%next => this%tail
+    ELSE IF (idx <= 1) THEN
+      this%nw = this%nw + 1
+      welt => this%head
+      allocate(this%head)
+      ASSIGN_DTSTR(value,this%head%value)
+      this%head%next => welt
+      this%head%next%prev => this%head
+    ELSE
+      IF (idx > (this%nw+1)/2) THEN
+        nx => this%tail 
+        DO i=1, this%nw - idx ; nx => nx%prev ; ENDDO
+      ELSE
+        nx => this%head 
+        DO i=1, idx-1 ; nx => nx%next ; ENDDO
+      ENDIF
+      pv => nx%prev
+      allocate(welt)
+      ASSIGN_DTSTR(value,welt%value)
+      welt%prev => pv ; welt%next => nx
+      pv%next => welt ; nx%prev => welt
+      this%nw = this%nw + 1
+    ENDIF
+    RETURN
+  END SUBROUTINE words_insert
+
+  SUBROUTINE words_append(this,value)
+    !! Append a word to the list of word
+    !! 
+    !! The method appends a word to the list of word. This is a convinient wrapper to 
+    !! [[string_op(module)::words_insert(subroutine)]] to add a new word at the beginning of the list.
+    OBJECT(words), INTENT(inout) :: this  !! A words object 
+    CHARACTER(len=*), INTENT(in) :: value !! A string to append
+    !CALL words_insert(this,this%nw+1,value)
+    type(word), pointer :: np
+
+    call words_insert(this,this%nw+1,value)
+
+    !! If the list is empty
+    !if (this%nw == 0) then
+    !  call ini_word(this, value)
+    !  return
+    !end if
+    !! Add new element to the end
+    !this%nw = this%nw + 1
+    !np => this%tail
+    !allocate(this%tail)
+    !this%tail%value = TRIM(value)
+    !this%tail%prev => np
+    !this%tail%prev%next => this%tail 
+    RETURN
+  END SUBROUTINE words_append
+
+  SUBROUTINE words_prepend(this,value)
+    !! Prepend a word to the list of word
+    !!
+    !! The method prepends a word to the list of word. This is a convinient wrapper to
+    !! [[string_op(module)::words_insert(subroutine)]] to add a new word at the end of the list.
+    OBJECT(words), INTENT(inout) :: this  !! A words object 
+    CHARACTER(len=*), INTENT(in) :: value !! A string to prepend
+    CALL words_insert(this,0,value)
+    RETURN
+  END SUBROUTINE words_prepend
+
+  FUNCTION words_get(this,idx,case) RESULT (res)
+    !! Get the word's value at given index
+    !! 
+    !! The method attempts to get the word's value at the given index. If index is out of range
+    !! an empty string is returned.
+    !! @note 
+    !! The returned string is always trimmed.
+    OBJECT(words), INTENT(in)              :: this
+      !! A words object reference
+    INTEGER, INTENT(in)                    :: idx
+      !! An integer with the index of a word in the list
+    CHARACTER(len=5), INTENT(in), OPTIONAL :: case 
+      !! An optional string with either 'upper' or 'lower' to get the value converted in the relevant case
+    CHARACTER(len=:), ALLOCATABLE :: res
+      !! The value of the word stored at given index in the list of words
+    TYPE(word), POINTER :: cur
+    cur => ws_get_ptr(this,idx)
+    IF (.not.associated(cur)) THEN
+      res = '' ; RETURN
+    ENDIF
+    IF (PRESENT(case)) THEN
+      IF (case == "upper") res = to_upper(cur%value) 
+      IF (case == "lower") res = to_lower(cur%value)
+    ELSE
+      res = TRIM(cur%value)
+    ENDIF
+    RETURN
+  END FUNCTION words_get
+
+  SUBROUTINE words_set(this,idx,value)
+    !! Set a new value to a word object in the list of words at given index
+    !!
+    !! The method sets a new word at given index. If index is out of range, the method simply does nothing.
+    OBJECT(words), INTENT(inout) :: this  !! A words object
+    INTEGER, INTENT(in)          :: idx   !! An integer with the index of the word object to modify in the list
+    CHARACTER(len=*), INTENT(in) :: value !! A string with the new value to set
+    TYPE(word), POINTER :: cur
+    cur => ws_get_ptr(this,idx)
+    IF (.NOT.ASSOCIATED(cur)) RETURN
+    cur%value = value
+  END SUBROUTINE words_set
+
+  FUNCTION words_get_max_width(this) RESULT(res)
+    !! Get the longest word's width in the words object
+    !!
+    !! The method computes and returns the longest (trimmed) word's width in the words object.
+    OBJECT(words), INTENT(in) :: this !! A words object 
+    INTEGER :: res                    !! An integer with the maximum width (0 if the list is empty)
+    TYPE(word), POINTER :: cur
+    res = 0
+    IF (this%nw == 0) RETURN
+    cur => this%head ; res = word_length(cur)
+    DO WHILE(ASSOCIATED(cur%next))
+      cur => cur%next
+      IF (word_length(cur) > res) res = word_length(cur)
+    ENDDO
+    RETURN
+  END FUNCTION words_get_max_width
+
+  FUNCTION words_get_total_width(this) RESULT(width)
+    !! Get the total width of all words stored in the list of words
+    !! 
+    !! The method computes and returns the total width of all words stored in 
+    !! the list of words.
+    !! @note 
+    !! Total width is computed using strings::word_length so it only takes
+    !! into account trimmed words (without trailing blanks)
+    !! @note 
+    !! If csi codes have been added to words elements they are counted in the width.
+    OBJECT(words), INTENT(in) :: this !! A words object
+    INTEGER :: width                  !! Total length of the list of words
+    TYPE(word), POINTER :: cur
+    width = 0
+    IF (this%nw == 0) RETURN
+    cur => this%head ; width = word_length(cur)
+    DO WHILE(ASSOCIATED(cur%next))
+      cur => cur%next
+      width = width + word_length(cur)
+    ENDDO
+    cur => null()
+    RETURN
+  END FUNCTION words_get_total_width
+
+  SUBROUTINE words_reverse(this)
+    !! Reverse the list of words in-place
+    OBJECT(words), INTENT(inout) :: this
+      !! A words object to reverse
+    TYPE(word), POINTER :: loop,iwc,iwp
+    IF (this%nw <= 1) RETURN
+    loop => this%head ; iwc=> this%head ; iwp=> null()
+    DO WHILE(ASSOCIATED(loop%next))
+      loop => loop%next
+      iwp => iwc%prev ; iwc%prev => iwc%next ; iwc%next => iwp
+      iwc => loop
+    ENDDO
+    iwp=>this%tail%prev ; this%tail%prev=>this%tail%next ; this%tail%next=>iwp
+    iwc => this%head ; this%head => this%tail ; this%tail => iwc
+    loop => null() ; iwc => null() ; iwp => null()
+    RETURN
+  END SUBROUTINE words_reverse
+
+  FUNCTION words_reversed(this) RESULT(res)
+    !! Get a reversed copy of the list of words
+    OBJECT(words), INTENT(in) :: this
+      !! A words object to reverse
+    TYPE(words) :: res
+      !! A reversed copy of the input list of words
+    TYPE(word),POINTER  :: cur
+    IF(this%nw == 0) RETURN
+    cur => this%tail
+    DO WHILE(ASSOCIATED(cur))
+      CALL words_append(res,cur%value)
+      IF (ASSOCIATED(cur,this%iter)) res%iter => res%tail 
+      cur => cur%prev
+    ENDDO
+    cur => null()
+    RETURN
+  END FUNCTION words_reversed
+
+  SUBROUTINE words_dump(this,lun)
+    !! Dump the list of words
+    !! 
+    !! The method dumps on the given logical unit the elements of the list one by line.
+    OBJECT(words), INTENT(in)     :: this
+      !! A words object to dump
+    INTEGER, INTENT(in), OPTIONAL :: lun
+      !! An optional integer with the printing logical unit. If not given, the list is dumped on 
+      !! standard output stream.
+    TYPE(word), POINTER :: cur
+    INTEGER             :: lu
+    IF (this%nw == 0) RETURN
+    lu=6 ; IF (PRESENT(lun)) lu = lun
+    cur => this%head
+    DO WHILE(ASSOCIATED(cur))
+      WRITE(lu,'(a)') TRIM(cur%value)
+      cur => cur%next
+    ENDDO
+    cur => null()
+    RETURN
+  END SUBROUTINE words_dump
+
+  FUNCTION words_to_string(this, delimiter,protect) RESULT(str)
+    !! Convert the list of words into a string
+    !!
+    !! The method converts the list of words into a string. In output, string is always
+    !! allocated even if the list is empty.
+    !!
+    !! If `protect` is set to .true. (default to .false.), each word is enclose between
+    !! double quotes. This option can be used to perform operation on the string before
+    !! setting it back as values of the list of words.
+    OBJECT(words), INTENT(in)              :: this
+      !! A words object 
+    CHARACTER(len=*), INTENT(in), OPTIONAL :: delimiter
+      !! An optional string used as delimiter between each words
+    LOGICAL, INTENT(in), OPTIONAL :: protect
+      !! Optional control flag with .true. to protect each word during concatentation.
+    CHARACTER(len=:), ALLOCATABLE :: str
+      !! An allocatable string with the list of words joined by the given delimiter (if any)
+    TYPE(word), POINTER :: cur
+    LOGICAL :: zprotect
+    zprotect = .false. ; IF (PRESENT(protect)) zprotect = protect
+    str = ''
+    IF (this%nw == 0) RETURN
+    cur => this%head
+    DO WHILE(ASSOCIATED(cur))
+      IF (zprotect) THEN
+        str=str//'"'//TRIM(cur%value)//'"'
+      ELSE
+        str=str//TRIM(cur%value)
+      ENDIF
+      IF (PRESENT(delimiter).AND..NOT.ASSOCIATED(cur,this%tail)) &
+        str=str//delimiter
+      cur => cur%next
+    ENDDO
+    RETURN
+  END FUNCTION words_to_string
+
+  FUNCTION words_to_vector(this,ret) RESULT(ok)
+    !! Convert the list of words into a vector of strings
+    !!
+    !! The method attempts to convert the list of words in a vector of strings.
+    !! If _this_ list of words is empty, the output vector is allocated with 0 elements and the method returns
+    !! .false., otherwise it returns .true.
+    !! @note
+    !! If elements in __this__ words object are wider than [[string_op(module):st_slen(variable)]], output 
+    !! values will be truncated.
+    OBJECT(words), INTENT(in)                                      :: this
+      !! A words object reference
+    CHARACTER(len=st_slen), INTENT(out), ALLOCATABLE, DIMENSION(:) :: ret
+      !! An allocatable vector of assumed length string with the words of __this__
+    LOGICAL             :: ok
+      !! Return status.
+    INTEGER             :: l,mw
+    TYPE(word), POINTER :: iw
+    ok = .true.
+    l = words_length(this)
+    IF (l == 0) THEN
+      ALLOCATE(ret(0))
+      ok = .false.
+      RETURN
+    ENDIF
+    ALLOCATE(ret(l)) ; mw = LEN(ret(l))
+    ret(1:l) = ' ' ! really needed ?
+    iw => this%head ; l=1
+    DO WHILE(ASSOCIATED(iw))
+       ret(l) = TRIM(iw%value) ; l=l+1 ; iw => iw%next
+    ENDDO
+  END FUNCTION words_to_vector
+
+  FUNCTION words_pop(this,idx,move_forward) RESULT(value)
+    !! Pop a word in the list of words
+    !!
+    !! The method removes the word of the list at given index and returns it. If no index is given, 
+    !! last word of the list is removed.
+    !!
+    !! If the index is out of range, the method does nothing and returns an empty string.
+    !!
+    !! By default, if the iterator is located on the item to be removed, it is moved backward before
+    !! deletion occurs. If __move\_forward__ is set to .true., the iterator is moved forward.
+    OBJECT(words), INTENT(inout)  :: this
+     !! A words object
+    INTEGER, INTENT(in), OPTIONAL :: idx
+      !! Optional index of the word to delete
+    LOGICAL, INTENT(in), OPTIONAL :: move_forward 
+      !! Move the iterator forward if needed. By default the iterator is moved backward. 
+    CHARACTER(len=:), ALLOCATABLE :: value
+      !! The word's value at given index 
+    LOGICAL             :: zforward
+    INTEGER             :: zidx
+    TYPE(word), POINTER :: cur
+    zidx=words_length(this) ; IF (PRESENT(idx)) zidx = idx
+    zforward = .false. ; IF (PRESENT(move_forward)) zforward = move_forward
+    cur => ws_get_ptr(this,zidx)
+    IF (.NOT.ASSOCIATED(cur)) THEN
+      value = '' ; RETURN
+    ELSE IF (ASSOCIATED(cur,this%iter)) THEN
+      IF (zforward) THEN
+        CALL words_next(this)
+      ELSE
+        CALL words_previous(this)
+      ENDIF
+    ENDIF
+    value = TRIM(cur%value)
+    CALL disconnect_word(cur)
+    DEALLOCATE(cur)
+    this%nw = this%nw - 1
+    RETURN
+  END FUNCTION words_pop
+
+  SUBROUTINE words_remove(this,idx,move_forward)
+    !! Remove the word of the list at given index
+    !!
+    !! The method removes the word of the list at given index. If no index is given, last word 
+    !! of the list is removed.
+    !!
+    !! If the index is out of range, the method does nothing.
+    !!
+    !! By default, if the iterator is located on the item to be removed, it is moved backward before
+    !! deletion occurs. If __move\_forward__ is set to .true., the iterator is moved forward.
+    OBJECT(words), INTENT(inout)  :: this
+      !! A words object
+    INTEGER, INTENT(in), OPTIONAL :: idx
+      !! Index of the word to delete
+    LOGICAL, INTENT(in), OPTIONAL :: move_forward
+      !! Move the iterator forward if needed. By default the iterator is moved backward. 
+    LOGICAL             :: zforward 
+    INTEGER             :: zidx
+    TYPE(word), POINTER :: cur
+    zidx=words_length(this) ; IF(PRESENT(idx)) zidx = idx
+    zforward = .false. ; IF (PRESENT(move_forward)) zforward = move_forward
+    cur => ws_get_ptr(this,idx)
+    IF (.NOT.ASSOCIATED(cur)) THEN
+      RETURN
+    ELSE IF (ASSOCIATED(cur,this%iter)) THEN
+      IF (zforward) THEN
+        CALL words_next(this)
+      ELSE
+        CALL words_previous(this)
+      ENDIF
+    ENDIF
+    CALL disconnect_word(cur)
+    DEALLOCATE(cur)
+    this%nw = this%nw - 1
+    RETURN
+  END SUBROUTINE words_remove
+
+  SUBROUTINE words_next(this)
+    !! Go to the next word in the list
+    OBJECT(words), INTENT(inout) :: this !! A words object
+    IF (ASSOCIATED(this%iter)) this%iter => this%iter%next
+  END SUBROUTINE words_next
+
+  SUBROUTINE words_previous(this)
+    !! Go to the previous word in the list
+    OBJECT(words), INTENT(inout) :: this !! A words object
+    IF (ASSOCIATED(this%iter)) this%iter => this%iter%prev
+  END SUBROUTINE words_previous
+
+  FUNCTION words_valid(this) RESULT(ret)
+    !! Check if the current iterated word is valid 
+    OBJECT(words), INTENT(in) :: this !! A words object
+    LOGICAL :: ret                    !! A logical flag with .true. if the current iterated word is valid
+    ret = associated(this%iter)
+  END FUNCTION words_valid
+
+  FUNCTION words_current(this) RESULT(wrd)
+    !! Get current word value
+    OBJECT(words), INTENT(in) :: this
+      !! A words object
+    CHARACTER(len=:), ALLOCATABLE :: wrd
+      !! A string with the value of the current word or __an unallocated string__ if current word 
+      !! is not valid (see [[string_op(module):words_valid(function)]]).
+    IF (ASSOCIATED(this%iter)) THEN
+      wrd = this%iter%value
+    ENDIF
+  END FUNCTION words_current
+
+  SUBROUTINE words_reset(this,to_end)
+    !! Reset the iterator 
+    !!
+    !! The method resets the iterator either at the beginning or at the end of the list of words 
+    !! (if __to_end__ is set to .true.).
+    OBJECT(words), INTENT(inout)  :: this   !! A words object 
+    LOGICAL, INTENT(in), OPTIONAL :: to_end !! An optional logical flag with .true. to reset the iterator at the end of the list
+    this%iter => this%head
+    IF (PRESENT(to_end)) THEN
+      IF (to_end) this%iter => this%tail
+    ENDIF
+  END SUBROUTINE words_reset
+
+  ! Fancy string methods
+  ! --------------------
+
+  FUNCTION tokenize(str,vector,delimiter,merge,protect) RESULT(ok)
+    !! Tokenize a string.
+    CHARACTER(len=*), INTENT(in)                             :: str
+      !! A string to tokenize
+    CHARACTER(len=*), INTENT(out), DIMENSION(:), ALLOCATABLE :: vector
+      !! An allocatable vector of strings with the tokens found. If string cannot be tokenized, 
+      !! the vector is __allocated to 0 elements__ and the method returns .false..
+    CHARACTER(len=*), INTENT(in), OPTIONAL                   :: delimiter
+      !! An optional string with the words delimiters. It is set to blank space by default. 
+      !! Note that each character is seen as a single delimiter.
+    LOGICAL, INTENT(in), OPTIONAL                            :: merge
+      !! An optional boolean control flag with .true. that instructs the method whether to 
+      !! merge or not successive delimiters. Default to .false.
+    LOGICAL, INTENT(in), OPTIONAL                            :: protect
+      !! An optional boolean flag with .true. to indicate that delimiter characters between 
+      !! quotes are protected. Default to .true.
+    LOGICAL :: ok
+      !! Return status (.true. on success).
+    CHARACTER(len=:), ALLOCATABLE :: seps
+    TYPE(words)                   :: tmp
+    LOGICAL                       :: zmerge,zprotect
+    TYPE(word), POINTER           :: wrd
+    integer                       :: i,nw
+    ok = .true.
+    zmerge = .false. ; zprotect = .true. ; seps = ' ' 
+    IF (PRESENT(merge)) zmerge = merge
+    IF (PRESENT(protect)) zprotect = protect
+    IF (PRESENT(delimiter)) THEN
+      IF (LEN(delimiter) > 0 ) seps = delimiter
+    ENDIF
+    call words_extend(tmp,str,seps,zmerge,zprotect)
+    nw = tmp%nw
+    i =  1
+    ALLOCATE(vector(tmp%nw))
+    IF (nw > 0) THEN
+      wrd => tmp%head
+      DO WHILE(ASSOCIATED(wrd))
+        vector(i) = TRIM(wrd%value)
+        wrd => wrd%next
+        i = i+1
+      ENDDO
+    ELSE
+      ok = .false.
+    ENDIF
+    call words_clear(tmp)
+    RETURN
+  END FUNCTION tokenize
+
+  FUNCTION remove_quotes(str) RESULT(ostr)
+    !! Strips outer quotes from string
+    !!
+    !! The function removes only external quotes from the input string
+    !! and returns the result in an allocatable string.
+    !! Quotes are removed only if they are the first and last non blank
+    !! characters. Either double and single quotes are stripped without distinction.
+    !! The output string is trimmed from leading and trailing blank spaces (after quotes removal !)
+    CHARACTER(len=*), INTENT(in)  :: str  !! A string to check
+    CHARACTER(len=:), ALLOCATABLE :: ostr !! A string without external quotes (if any).  
+    CHARACTER(len=1), PARAMETER   :: sq=CHAR(39), dq=CHAR(34)
+    CHARACTER(len=2), PARAMETER   :: dsq=CHAR(39)//CHAR(34)
+    INTEGER                       :: i, j
+    IF (LEN_TRIM(str) == 0) RETURN
+    ostr = TRIM(ADJUSTL(str))
+    i = SCAN(ostr,sq//dq) ; j = SCAN(ostr,sq//dq,.true.)
+    IF (i == j) RETURN
+    IF (i /= 1) i = 0
+    IF (j /= LEN(ostr)) j = LEN(ostr)+1
+    ostr = TRIM(ostr(i+1:j-1))
+    RETURN
+  END FUNCTION remove_quotes
+
+  FUNCTION string_is(str) RESULT(ret)
+    !! Check if string represents an intrinsic type
+    !!
+    !! The method checks if the given string represents an intrinsic type. Both logical and complex type 
+    !! are checked in a strict way :
+    !!
+    !! - A string is a logical if it is one of the following value: __.false.__, __.true.__, __F__, __T__.
+    !! - A string is potentially a complex if it has the following format: __(\*\*\*,\*\*\*)__ where 
+    !!   __\*\*\*__ is checked to see wether it is numerical or not.
+    !!
+    !! Valid numerical values can take the following forms:
+    !! ```
+    !!   [0-9]
+    !!   [0-9]*.?[0-9]*?([ed][+-]?[0-9]+)?
+    !! ```
+    !! Obviously if returned value is greater than 3, the string can be converted in 
+    !! floating point value.
+    !!
+    !! Empty input string is simply considered to be of string type !  
+    CHARACTER(len=*), INTENT(in) :: str
+      !! A string to check
+    INTEGER :: ret
+      !! An integer with the intrinsic type related to the string.
+      !!
+      !! Types are one of the following parameters
+      !!
+      !! - [[string_op(module):st_string(variable)]] (1) for string
+      !! - [[string_op(module):st_logical(variable)]] (2) for logical
+      !! - [[string_op(module):st_complex(variable)]] (3) for complex
+      !! - [[string_op(module):st_integer(variable)]] (4) for integer
+      !! - [[string_op(module):st_real(variable)]] (5) for floating point value
+    CHARACTER(len=:), ALLOCATABLE :: zs,zzs
+    INTEGER :: j,l
+    ret = 1 ; IF (LEN_TRIM(str) == 0) RETURN
+    zs = to_lower(TRIM(ADJUSTL(str))) ; j = INDEX(zs,',') ; l = len(zs)
+    IF (zs(1:1)=='('.AND.zs(l:l) == ')'.AND.j==INDEX(zs,',')) THEN
+      IF (j == 2 .OR. j == l-1) RETURN
+      zzs = TRIM(ADJUSTL(zs(2:j-1))) ; IF (what_(zzs) < 3) RETURN
+      zzs = TRIM(ADJUSTL(zs(j+1:l-1))) ; ret = what_(zzs)
+      IF (ret > 3) THEN ; ret = 3 ; ELSE ; ret = 1 ; ENDIF
+    ELSE
+      ret = what_(zs)
+    ENDIF
+    CONTAINS
+      FUNCTION what_(s) RESULT(is)
+        !! Check if the given string is numerical, logical or a simple string
+        !! @note
+        !! Input string should be in lower case, otherwise, the method will give a a wrong result.
+        !! @warning
+        !! The test performed for logical checking is quite strict : A string is considered as logical
+        !! if and only if it is one of the following values : __.false.__, __.true.__, __F__, __T__.
+        CHARACTER(len=*), INTENT(in) :: s
+          !! A string to check
+        INTEGER :: is
+          !! An integer with : __1__ for string, __2__ for logical, __4__ for integer and __5__ for real
+        LOGICAL                      :: dec,fdot,fexp
+        INTEGER                      :: i
+        CHARACTER(len=24), PARAMETER :: aset='abcfghijklmnopqrstuvwxyz'
+        CHARACTER(len=10), PARAMETER :: iset='1234567890'
+        CHARACTER(len=2),  PARAMETER :: dset='ed'
+        CHARACTER(len=2),  PARAMETER :: sset='+-'
+        CHARACTER(len=7),  PARAMETER :: slog(4) = (/'.true. ','.false.',&
+                                                    't      ','f      '/)
+        is = -1 ; dec = .false. ; fdot = dec ; fexp = fdot
+        DO i = 1,LEN(s)
+          IF (i == 1) THEN
+            ! string does not start by [+-\.\d]
+            IF (VERIFY(s(i:i),'.'//iset//sset) /= 0) THEN
+              is = 1 ; EXIT
+            ENDIF
+            ! update control flag for decimal part
+            dec = s(i:i) == '.' ; fdot = dec
+          ELSE
+            ! check if char is in [a-z]
+            IF(VERIFY(s(i:i),aset) == 0) THEN
+              dec=.false. ; is = 1 ; EXIT
+            ELSE IF (s(i:i) == '.') THEN
+              ! check for dot in decimal/exponent part (==> not a number
+              IF (fdot.OR.fexp) THEN
+                dec = .false. ; is = 1 ; EXIT
+              ENDIF
+            ELSE IF (VERIFY(s(i:i),dset)==0) THEN
+              IF (fexp) THEN
+                dec = .false. ; is = 1 ; EXIT
+              ENDIF
+            ELSE IF (VERIFY(s(i:i),sset) == 0) THEN
+              IF (VERIFY(s(i-1:i-1),dset) /= 0) THEN
+                dec = .false. ; is = 1 ; EXIT
+              ENDIF
+            ENDIF
+            fdot = (fdot .OR. s(i:i) == '.')
+            fexp = (fexp .OR. VERIFY(s(i:i), dset) == 0)
+          ENDIF
+        ENDDO
+        ! it is a string
+        IF (is == 1) THEN
+          ! but have the format of a logical
+          IF (any(slog == s)) is = 2
+        ELSE
+          IF ((fexp.AND.SCAN(s(LEN(s):LEN(s)),dset) /= 0)) THEN
+            is = 1
+          ELSE
+            is = 4
+            IF (fdot.OR.fexp) is = 5
+          ENDIF
+        ENDIF
+      END FUNCTION what_
+  END FUNCTION string_is
+
+  FUNCTION format_string(str,idt1,idto) RESULT(output)
+    !! Format the given string
+    !!
+    !! This function only replaces all '\\n' escape sequence in the given string by NEW_LINE() character.
+    !! The output string is eventually indented if optional arguments are set.
+    !! @warning
+    !! __idto__ is relative to __idt1__ !
+    CHARACTER(len=*), INTENT(in)  :: str     !! The string to format
+    INTEGER, INTENT(in), OPTIONAL :: idt1, & !! An optional integer with the indentation level of the first output line (default to 0)
+                                     idto    !! An optional integer with the indentation level of all other output lines (default to 0)
+    CHARACTER(len=:), ALLOCATABLE :: output  !! An allocatable string with the output formatted string.
+    ! - LOCAL
+    INTEGER :: i,c,ti,mx
+    CHARACTER(len=:), ALLOCATABLE :: idts
+    IF (LEN_TRIM(str) == 0) THEN
+      ALLOCATE(output,source='') ; RETURN
+    ENDIF
+    i=0 ; IF (PRESENT(idt1)) i = MAX(i,idt1) 
+    ALLOCATE(CHARACTER(len=i) :: output) 
+    IF (i > 0) output(1:i) = CHAR(32) 
+    ! i0 is relative to i1 and must be >= 0
+    IF (PRESENT(idto)) i = MAX(i+idto,0)
+    ALLOCATE(CHARACTER(len=i+1) :: idts)
+    idts(1:1) = NEW_LINE('A') ; IF (i>1) idts(2:) = CHAR(32) 
+    ! Builds output string 
+    c=1 ; mx = LEN_TRIM(str)
+    i = INDEX(str(c:),'\n') ; ti = c+i-1
+    IF (i == 0) THEN
+      output=output//TRIM(str(ti+1:mx)) 
+    ELSE
+      output=output//TRIM(str(c:ti-1)) ; c=ti+2 
+      DO
+        i = INDEX(str(c:),"\n") ; ti = c+i-1
+        IF (i == 0) THEN
+          output=output//TRIM(str(ti+1:mx)) ; c = mx+1 
+        ELSE
+          output=output//idts//str(c:ti-1) ; c = ti+2
+        ENDIF
+        IF (c > mx) EXIT
+      ENDDO
+    ENDIF
+    ! print a newline if we have \n at the end of the string
+    IF (INDEX(TRIM(str),'\n',.true.) == mx-1.AND.TRIM(str) /= '\n') &
+    output=output//idts(1:1)
+  END FUNCTION format_string
+
+  FUNCTION format_paragraph(str,width,idt1,idto) RESULT(output)
+    !! Split and format a string over several lines 
+    !! 
+    !! The function splits an input string in words so output lines fit (almost) in __width__ characters. 
+    !! The method handles indentation level (defined as leading blank spaces). It also accounts for known 
+    !! csi (see [[string_op(module):attributes(variable)]]).
+    !! @note
+    !! Words are considered indivisible and thus output lines can sometimes exceed the maximum width if 
+    !! there is not enough space to put a word (with the associated indentation if given). The default
+    !! behavior in that case is to print the word in a new line (with the correct leading blank spaces).
+    !! @warning
+    !! If __width__, __idt1__ and/or __idto__ have inconsistent values (e.g. __width__ <= __idt1__), the
+    !! method still computes the paragraph, but each words will be set on a new line with the appropriate
+    !! indentation.
+    CHARACTER(len=*), INTENT(in)  :: str    !! string with the content to split 
+    INTEGER, INTENT(in)           :: width  !! An positive integer with the maximum width of a line
+    INTEGER, INTENT(in), OPTIONAL :: idt1   !! An optional integer with the indentation level of the first output line
+    INTEGER, INTENT(in), OPTIONAL :: idto   !! An optional integer with the indentation level of the other output lines
+    CHARACTER(len=:), ALLOCATABLE :: output !! An allocatable string with the output content
+    CHARACTER(len=:), ALLOCATABLE :: idts,zs
+    INTEGER                       :: l1,lo,zmx,zw,cc,j,jj,l
+    zw = abs(width) ; zs = strip_newline(str)
+    zmx = LEN_TRIM(zs)
+    IF (zmx == 0) THEN
+      ALLOCATE(output,source='') ; RETURN
+    ENDIF
+    l1=0 ; IF (PRESENT(idt1)) l1 = MAX(l1,idt1)
+    ALLOCATE(CHARACTER(len=l1) :: output)
+    IF (l1 > 0) output(1:l1) = CHAR(32)
+    lo=l1 ; IF (PRESENT(idto)) lo = MAX(l1+idto,0)
+    ALLOCATE(CHARACTER(len=lo+1) :: idts)
+    idts(1:1) = NEW_LINE('A') ; IF (lo>=1) idts(2:len(idts)) = CHAR(32)
+    ! Prints a message if user is just stupid...
+    IF (lo+1 > zw .OR. l1+1 > zw) THEN
+      output = str ; RETURN
+    ENDIF
+    ! check if can just return the string as is 
+    IF (zmx + l1 <= zw) THEN
+      output=output//TRIM(zs) ; RETURN
+    ENDIF
+    j=1 ; jj=1+l1 
+    DO 
+      ! Gets next blank in input string
+      cc = INDEX(TRIM(zs(j:)),CHAR(32))
+      ! no more blank
+      ! Gets total length of csi between zs(j:j+cc-1)
+      ! this value will be substracted to each length test
+      IF (cc == 0) THEN
+        l = csis_length(zs(j:)) 
+        IF (jj-1+LEN_TRIM(zs(j:))-l > zw) THEN
+          output = output//idts
+        ENDIF
+        output=output//TRIM(zs(j:))
+        EXIT ! we are at the last word : we must exit the infinite loop !
+      ELSE
+        l = csis_length(zs(j:j+cc-1)) 
+        IF (cc+jj-1-l > zw) THEN
+          output=output//idts//zs(j:j+cc-1) ; jj = lo+1+cc+1 - l
+        ELSE
+          output=output//zs(j:j+cc-1) ; jj = jj + cc - l
+        ENDIF
+      ENDIF
+      j = j + cc
+    ENDDO
+    CONTAINS
+    FUNCTION csis_length(str) RESULT(value)
+      ! - DUMMY
+      CHARACTER(len=*), INTENT(in) :: str
+      ! - RESULT
+      INTEGER :: value
+      ! - LOCAL
+      INTEGER :: jc,iesc,im
+      LOGICAL :: tcsi
+      value = 0 
+      jc=1
+      DO 
+        IF (jc>LEN(str)) EXIT
+        ! search for escape
+        iesc = INDEX(str(jc:),CHAR(27))
+        IF (iesc == 0) EXIT 
+        ! search for m
+        im = INDEX(str(jc+iesc:),"m")
+        ! no m in the string after ESC --> this could not be a csi
+        IF (im == 0) EXIT
+        ! check if this is really a csi and updates length
+        tcsi = is_csi(str(jc+iesc-1:jc+iesc+im-1))
+        jc = jc + iesc 
+        IF (tcsi) THEN
+          value=value+im+1
+          jc=jc+im
+        ENDIF
+      ENDDO
+    END FUNCTION csis_length
+  END FUNCTION format_paragraph
+
+  FUNCTION strip_newline(str,rpl) RESULT(stripped)
+    !! Replace newline escape sequences by spaces
+    !!
+    !! The function replaces newline (both '\\n' escape sequence and Fortran NEW_LINE() character) in the 
+    !! given string and returns the resulting string.
+    CHARACTER(len=*), INTENT(in)           :: str !! A string to process
+    CHARACTER(len=1), INTENT(in), OPTIONAL :: rpl !! A optional single character used as substitution of escape sequences (blank space by default)
+    CHARACTER(len=:), ALLOCATABLE :: stripped     !! An allocatable string with all newline sequences replaced by blank space or __rpl__ if given 
+    CHARACTER(len=1) :: zrp
+    INTEGER          :: i, j, ns 
+    zrp = CHAR(32) ; IF(PRESENT(rpl)) zrp = rpl
+    IF (str == NEW_LINE('A')) THEN
+      stripped = zrp ; RETURN 
+    ENDIF
+    ns = LEN_TRIM(str)
+    IF (ns == 0) THEN
+      ALLOCATE(stripped,source='') ; RETURN
+    ENDIF
+    ALLOCATE(CHARACTER(len=ns) :: stripped) ; stripped(1:ns) = CHAR(32)
+    i=1 ; j=1
+    DO 
+      IF (str(i:i) == NEW_LINE('A')) THEN
+        stripped(j:j) = zrp 
+      ELSE IF (i < ns) THEN
+          IF (str(i:i+1) == "\n") THEN
+            stripped(j:j) = zrp ; i=i+1
+          ELSE
+            stripped(j:j) = str(i:i) 
+          ENDIF
+      ELSE
+        stripped(j:j) = str(i:i) 
+      ENDIF
+      j=j+1 ; i=i+1
+      IF (i > ns .OR. j > ns) EXIT
+    ENDDO
+    IF (j < ns) stripped = stripped(1:j)
+    RETURN
+  END FUNCTION strip_newline
+
+  FUNCTION str_length(str) RESULT(res)
+    !! Get the length of the string object
+    !! 
+    !! The method computes the length of the string. It differs from LEN intrinsic function as
+    !! it does not account for extra-characters of csi codes.
+    CHARACTER(len=*), INTENT(in) :: str !! String to process
+    INTEGER :: res                      !! The actual length of string (i.e. does not account for csi codes)
+    CHARACTER(len=:), ALLOCATABLE :: tmp
+    res = 0 
+    IF (LEN(str) /= 0) THEN
+      tmp = reset_csi(str)
+      res = LEN(tmp)
+      DEALLOCATE(tmp)
+    ENDIF
+    RETURN
+  END FUNCTION str_length
+
+  FUNCTION to_lower(str1) RESULT(str)
+    !! Convert the string in lower case
+    !!
+    !! The method converts the input string in lower case and accounts for
+    !! possible csi codes in the string.
+    CHARACTER(len=*), INTENT(in) :: str1 !! Input string to convert
+    CHARACTER(len=:), ALLOCATABLE :: str !! A copy of the string in lower case
+    INTEGER :: i,ic
+    IF (LEN(str1) /= 0) THEN
+      str = str1
+      DO i = 1, len(str1)
+        ic = ichar(str1(i:i))
+        IF (ic >= 65 .AND. ic < 90) str(i:i) = char(ic + 32)
+      ENDDO
+    ELSE
+      str=''
+    ENDIF
+  END FUNCTION to_lower
+
+  FUNCTION to_upper(str1) RESULT(str)
+    !! Convert the string in upper case
+    !!
+    !! The method converts the input string in upper case and accounts for
+    !! possible csi codes in the string.
+    CHARACTER(len=*), INTENT(in) :: str1 !! Input string to convert
+    CHARACTER(len=:), ALLOCATABLE :: str !! A copy of the string in upper case
+    INTEGER :: j,i,ic,icsi,lcsi
+    IF (LEN(str1) > 0) THEN
+      str = str1 
+      i = 1
+      DO
+        IF (i > LEN(str)) EXIT
+        icsi = str_index_of_csi(str(i:),lcsi)
+        IF (icsi == 0) THEN
+          ! no more csi the end of string is upper case converted
+          DO j=i,LEN(str)
+            ic = ichar(str(j:j))
+            IF (ic >= 97 .AND. ic < 122) str(j:j) = char(ic-32)
+          ENDDO
+          RETURN
+        ELSE IF (icsi == 1) THEN
+          i = i + lcsi
+        ELSE IF (icsi > 1) THEN
+          ! csi is not the first word: we convert in upper case until its
+          ! position THEN copy the csi and get back in the loop
+          DO j=i,i+icsi-2
+            ic = ichar(str(j:j))
+            IF (ic >= 97 .AND. ic < 122) str(j:j) = char(ic-32)
+          ENDDO
+          i = i + icsi + lcsi-1 
+        ENDIF
+      ENDDO
+    ELSE
+      str=''
+    ENDIF
+  END FUNCTION to_upper
+
+ FUNCTION str_remove(string,substring,back,all) RESULT(str)
+   !! Remove substring from current string 
+   !! 
+   !! The function removes the first occurence of __substring__ in __string__ or all 
+   !! its occurences if __all__ is explicitly set to .true..
+    CHARACTER(len=*), INTENT(in)  :: string    !! A string to search in
+    CHARACTER(len=*), INTENT(in)  :: substring !! A string to search and removes from __string__
+    LOGICAL, INTENT(in), OPTIONAL :: back, &   !! An optional boolean flag with .true. to begin search at the end of the string
+                                     all       !! An optional boolean flag with .true. to remove all occurences of __substring__
+    CHARACTER(len=:), ALLOCATABLE :: str       !! An allocatable string with __substring__ occurence(s) removed
+    LOGICAL :: zb,za
+    INTEGER :: is,j,zboff
+    str=''
+    zb = .false. ; za = .false.
+    IF (PRESENT(back)) zb = back
+    IF (PRESENT(all)) za = all
+    IF (za) zb=.false.
+    zboff = 0 ; IF (zb) zboff = 1
+    IF (LEN(string) == 0) RETURN
+    j=1 
+    DO 
+      IF (j>LEN(string)) EXIT
+      ! search for substring
+      is = INDEX(string(j:),substring,back=zb)
+      IF (is == 0) THEN
+        ! substring is not found : we get the last part of the string and return
+        str = str//string(j:) ; RETURN 
+      ELSE IF (is == 1) THEN
+        j = j + LEN(substring)
+      ELSE
+        ! substring is not at the begin of the string : saves the string
+        str = str//string(j:j+is-2)
+        j = j + is+LEN(substring)-1
+      ENDIF
+      ! if we only want to str_remove ONE occurence we exit if substring
+      ! has been found
+      IF (.NOT.(is==0.OR.za)) EXIT 
+    ENDDO
+    IF (j <= LEN(string).AND..NOT.zb) str=str//string(j:)
+    RETURN 
+  END FUNCTION str_remove
+
+ FUNCTION str_replace(string,old,new,back,all) RESULT(str)
+    !! Replace substring from current string 
+    !!
+    !! The function replaces the first occurence of __old__ in __string__ by 
+    !! __new__ or all its occurence(s) if __all__ is explicitly set to .true..
+    CHARACTER(len=*), INTENT(in)  :: string  !! A string to search in
+    CHARACTER(len=*), INTENT(in)  :: old,  & !! A string to search and replace
+                                     new     !! A string to substitute to __old__
+    LOGICAL, INTENT(in), OPTIONAL :: back, & !! An optional boolean flag with .true. to begin search at the end of the string
+                                     all     !! An optional boolean flag with .true. to replace all occurences of __old__
+    CHARACTER(len=:), ALLOCATABLE :: str     !! An allocatable string with occurence(s) of __old__ replaced by __new__
+    LOGICAL :: zb,za
+    INTEGER :: is,j
+    str=''
+    zb = .false. ; za = .false.
+    IF (PRESENT(back)) zb = back
+    IF (PRESENT(all)) za = all
+    IF (za) zb = .NOT.za
+    IF (LEN(string) == 0) RETURN 
+    j=1 
+    DO 
+      IF (j>LEN(string)) EXIT
+      ! search for "old"
+      is = INDEX(string(j:),old,back=zb)
+      IF (is == 0) THEN
+        ! "old" is not found : we get the last part of the string and return
+        str = str//string(j:) ; RETURN 
+      ELSE IF (is == 1) THEN
+        str = str//new
+        j = j + LEN(old)
+      ELSE
+        ! "old" is not at the begin of the string : saves the string
+        str = str//string(j:j+is-2)//new
+        j = j + is + LEN(old) - 1 
+      ENDIF
+      IF (.NOT.(is==0.OR.za)) EXIT 
+    ENDDO
+    IF (j <= LEN(str)) str=str//string(j:)
+    RETURN 
+  END FUNCTION str_replace
+
+  FUNCTION endswith(string,substring,icase) RESULT(ret)
+    !! Check if string ends by substring 
+    CHARACTER(len=*), INTENT(in)  :: string
+      !! @param[in] string A string to check
+    CHARACTER(len=*), INTENT(in)  :: substring
+      !! A string to search in __string__
+    LOGICAL, INTENT(in), OPTIONAL :: icase 
+      !! An optional boolean flag with .true. to perform insensitive case search
+    LOGICAL :: ret
+      !! .true. if __string__ ends by __substring__, .false. otherwise.
+    CHARACTER(len=:), ALLOCATABLE :: zthis,zstr
+    INTEGER                       :: idx 
+    LOGICAL                       :: noc 
+    ret = .false.
+    noc = .false. ; IF (PRESENT(icase)) noc = icase
+    IF (LEN(string) == 0 .OR. LEN(substring) == 0) RETURN
+    zthis = reset_csi(string) ; zstr=reset_csi(substring)
+    IF (noc) THEN
+      idx = INDEX(to_lower(zthis),to_lower(zstr),.true.)
+    ELSE
+      idx = INDEX(zthis,zstr,.true.)
+    ENDIF
+    IF (idx == 0.OR.idx+str_length(zstr)-1 /= str_length(zthis)) RETURN
+    ret=.true.
+  END FUNCTION endswith
+
+  FUNCTION startswith(string,substring,icase) RESULT(ret)
+    !! Check if string starts by substring 
+    CHARACTER(len=*), INTENT(in)  :: string
+      !! A string to check
+    CHARACTER(len=*), INTENT(in)  :: substring
+      !! A string to search in __string__
+    LOGICAL, INTENT(in), OPTIONAL :: icase 
+      !! An optional boolean flag with .true. to perform insensitive case search
+    LOGICAL :: ret
+      !! .true. if __string__ starts by __substring__, .false. otherwise.
+    CHARACTER(len=:), ALLOCATABLE :: zthis,zstr
+    INTEGER                       :: idx 
+    LOGICAL                       :: noc 
+    ret = .false.
+    noc = .false. ; IF (PRESENT(icase)) noc = icase
+    IF (LEN(string) == 0 .OR. LEN(substring) == 0) RETURN
+    zthis = reset_csi(string) ; zstr=reset_csi(substring)
+    IF (noc) THEN
+      idx = INDEX(to_lower(zthis),to_lower(zstr))
+    ELSE
+      idx = INDEX(zthis,zstr)
+    ENDIF
+    IF (idx /= 1) RETURN
+    ret=.true.
+  END FUNCTION startswith
+
+  ! CSI related functions 
+  ! ---------------------
+
+  FUNCTION add_csi(string,attrs) RESULT(str)
+    !! Set csi attributes to the given string object
+    !!
+    !! The function adds csi (ANSI escape sequences) to the given string and
+    !! returns a copy of it.
+    CHARACTER(len=*), INTENT(in)      :: string
+      !! @param[in] string A string object reference 
+    INTEGER, INTENT(in), DIMENSION(:) :: attrs
+      !! A vector of integers with the code to add. Each __attrs__ value should refers to one i
+      !! of [[string_op(module):attributes(variable)]] values.
+    CHARACTER(len=:), ALLOCATABLE :: str
+      !! An allocatable string with new csi codes added.
+    INTEGER                       :: j,iesc,im
+    CHARACTER(len=:), ALLOCATABLE :: tmp,csi
+    CHARACTER(len=4), PARAMETER   :: rcsi = CHAR(27)//"[0m"
+    str=''
+    ! 1) Check for input string
+    IF (LEN(string) == 0) RETURN
+    ! 2) Removes last <ESC>[0m if any and initializes output string
+    ! we must remove only the last <ESC>[0m if any
+    IF (INDEX(string,rcsi,.true.) == LEN(string)-3) THEN
+      tmp = str_remove(string,rcsi,back=.true.)
+    ELSE
+      tmp = string
+    ENDIF
+    ! 3) Add all the given csi preceded by <ESC>[0m at the beginning of the string 
+    !    if it does not start by an ANSI sequence
+    IF (INDEX(tmp,CHAR(27)//"[") /= 1) &
+    tmp = str_add_to_csi(rcsi,attrs)//tmp
+    ! Loops on new string and updates csi codes
+    j=1 
+    DO 
+      IF (j>LEN(tmp)) EXIT
+      ! search for escape
+      iesc = INDEX(tmp(j:),CHAR(27))
+      IF (iesc == 0) THEN
+        ! no more ESC : cat until end of input string and exit
+        str = str//tmp(j:) ; EXIT
+      ELSE IF (iesc > 1) THEN
+        ! ESC is not first char: copy until ESC
+        str = str//tmp(j:j+iesc-2)
+      ENDIF
+      ! search for m
+      im = INDEX(tmp(j+iesc:),"m")
+      ! no m in the string after ESC --> copy string (INCLUDING ESC) and leave
+      IF (im == 0) THEN
+        str = str//tmp(j+iesc-1:)
+        RETURN
+      ENDIF
+      csi = tmp(j+iesc-1:j+iesc+im-1)
+      ! we have a csi: we add new codes to it
+      IF (is_csi(csi)) THEN
+        csi = str_add_to_csi(csi,attrs)
+      ENDIF
+      str = str//csi
+      j = j + iesc + im
+    ENDDO
+    IF (INDEX(str,rcsi,.true.) /= LEN(str)-3) str = str//rcsi
+    RETURN 
+  END FUNCTION add_csi
+
+  FUNCTION del_csi(string,attrs) RESULT(str)
+    !! Remove attributes to the given string
+    !!
+    !! The function removes list of csi (ANSI escape sequences) from the given 
+    !! string and returns a copy of it.
+    CHARACTER(len=*), INTENT(in)      :: string
+      !! Input string 
+    INTEGER, INTENT(in), DIMENSION(:) :: attrs 
+      !! A vector of integers with the code to remove. Each __attrs__ value should 
+      !! refers to one of [[string_op(module):attributes(variable)]] values.
+    CHARACTER(len=:), ALLOCATABLE :: str
+      !! An allocatable string with csi codes from __list__ removed
+    LOGICAL                                           :: ok
+    INTEGER                                           :: j,iesc,im
+    CHARACTER(len=:), ALLOCATABLE                     :: tmp,csi,csis
+    CHARACTER(len=st_slen), DIMENSION(:), ALLOCATABLE :: tks
+    CHARACTER(len=4), PARAMETER                       :: rcsi = CHAR(27)//"[0m"
+    str=''
+    IF (LEN(string) == 0) RETURN
+    ! remove last <ESC>[0m if found at the end of the string
+    IF (INDEX(string,rcsi,.true.) == LEN(string)-3) THEN
+      tmp = str_remove(string,rcsi,back=.true.)
+    ELSE
+      tmp = string
+    ENDIF
+    ! Loops on new string and updates csi codes
+    j=1 ; csis=""
+    DO 
+      IF (j>LEN(tmp)) EXIT
+      ! search for escape
+      iesc = INDEX(tmp(j:),CHAR(27))
+      IF (iesc == 0) THEN
+        ! no more ESC : cat until end of input string and exit
+        str = str//tmp(j:) ; EXIT
+      ELSE IF (iesc > 1) THEN
+        ! ESC is not first char: copy until ESC
+        str = str//tmp(j:j+iesc-2)
+      ENDIF
+      ! search for m
+      im = INDEX(tmp(j+iesc:),"m")
+      ! no m in the string after ESC --> copy string (INCLUDING ESC) and leave
+      IF (im == 0) THEN
+        str = str//tmp(j+iesc-1:)
+        RETURN
+      ENDIF
+      csi = tmp(j+iesc-1:j+iesc+im-1)
+      ! we have a csi: we add new codes to it
+      IF (is_csi(csi)) THEN
+        csi = str_del_from_csi(csi,attrs)
+      ENDIF
+      csis=csis//csi//"|"
+      str = str//csi
+      j = j + iesc + im
+    ENDDO
+    ! Add <ESC>[0m at the end of string if not found
+    IF (INDEX(str,rcsi,.true.) /= LEN(str)-3) str = str//rcsi
+    ! resets all attributes if we only have <ESC>[0m in final list 
+    ok = tokenize(csis(1:LEN(csis)-1),tks,"|") 
+    IF (ALL(tks == rcsi)) str = reset_csi(str)
+    DEALLOCATE(tks)
+    RETURN 
+  END FUNCTION del_csi
+
+  FUNCTION reset_csi(string) RESULT(str)
+    !! Reset all csi codes of the string
+    !! 
+    !! The method removes __all__ the known escape sequences from the input string.
+    CHARACTER(len=*), INTENT(in) :: string
+      !! Input string
+    CHARACTER(len=:), ALLOCATABLE :: str 
+      !! An allocatable string with the copy of input string stripped off csi codes.
+    INTEGER :: j,iesc,im
+    LOGICAL :: tcsi
+    str = ""
+    IF (LEN(string) == 0) RETURN 
+    j=1 
+    DO 
+      IF (j>LEN(string)) EXIT
+      ! search for escape
+      iesc = INDEX(string(j:),CHAR(27))
+      IF (iesc == 0) THEN
+        str = str//string(j:) ; EXIT
+      ENDIF
+      ! search for m
+      im = INDEX(string(j+iesc:),"m")
+      ! no m in the string after ESC --> copy string (INCLUDING ESC) and leave
+      IF (im == 0) THEN
+        str = str//string(j+iesc-1:)
+        RETURN
+      ENDIF
+      ! csi includes everything between ESC and m (excluding them):
+      ! to check for csi it should begin by [ and then be a list of integers
+      ! separated by ;
+      tcsi = is_csi(string(j+iesc-1:j+iesc+im-1))
+      IF (iesc > 1) THEN
+        str = str//string(j:j+iesc-2)
+      ENDIF
+      j = j + iesc ; IF (tcsi) j=j+im
+    ENDDO
+    RETURN 
+  END FUNCTION reset_csi
+
+  FUNCTION is_csi(value) RESULT(yes)
+    !! Check if string is a known csi
+    !! 
+    !! The function only check for known csi code which are defined in [[string_op(module):attributes(variable)]].
+    CHARACTER(len=*), INTENT(in) :: value
+      !! A Fortran intrinsic string to check
+    LOGICAL :: yes
+      !! .true. if it is a known csi, .false. otherwise
+    LOGICAL                                           :: ok
+    CHARACTER(len=:), ALLOCATABLE                     :: tmp 
+    TYPE(words)                                       :: wtks
+    CHARACTER(len=st_slen), DIMENSION(:), ALLOCATABLE :: stks
+    INTEGER, DIMENSION(:), ALLOCATABLE                :: nums
+    INTEGER                                           :: i
+    yes = .false.
+    IF (LEN(value) < 4) RETURN
+    tmp = value(3:len(value)-1)
+    call words_extend(wtks,tmp,';')
+    ok = words_to_vector(wtks,stks)
+    CALL ws_clear_sc(wtks)
+    IF (.NOT.ok) RETURN
+    ! if we cannot convert strings to integers : it is not a csi
+    IF (.NOT.from_string(stks,nums)) RETURN
+    DEALLOCATE(stks)
+    DO i=1, SIZE(nums)
+      IF (.NOT.ANY(attributes == nums(i))) RETURN
+    ENDDO
+    yes = .true.
+  END FUNCTION is_csi
+
+  FUNCTION str_add_to_csi(csi,list) RESULT(ncsi)
+    !! Add a new list of codes to the input csi string
+    !! 
+    !! The method adds all the csi codes given in __list__ that are known by the module and not
+    !! already present in the input csi.
+    CHARACTER(len=*), INTENT(in)      :: csi
+      !! A string with the input csi. It __must__ begin with "<ESC>[" and ends with "m".
+    INTEGER, INTENT(in), DIMENSION(:) :: list
+      !! A vector of integers with the csi code to add. Each value of __list__ should be one of 
+      !! [[string_op(module):attributes(variable)]] values. All unknown values are filtered out as well 
+      !! as csi code already present in input __csi__.
+    CHARACTER(len=:), ALLOCATABLE :: ncsi 
+      !! A new csi string or the input __csi__ if some "errors" occured (the input csi could not 
+      !! be tokenized or none of __list__ values are left after filtering).
+    LOGICAL                                            :: ok 
+    CHARACTER(len=st_slen), DIMENSION(:), ALLOCATABLE  :: tks
+    CHARACTER(len=:), ALLOCATABLE                      :: tmp
+    INTEGER, DIMENSION(:), ALLOCATABLE                 :: zlist,nums
+    INTEGER                                            :: i,j,ni,no
+    ! 1) Filter input list :
+    ! 1.1) Gets the list of current csi codes 
+    ncsi = csi(3:len(csi)-1) 
+    ok = tokenize(ncsi,tks,"; ",merge=.true.)
+    IF (.NOT.from_string(tks,nums)) THEN
+      ncsi = csi
+      RETURN
+    ENDIF
+    DEALLOCATE(tks)
+    ! 1.2) Filter input list of new flags to add 
+    ! counts number of valid flags
+    j=0 
+    DO i=1,SIZE(list) 
+      ! new flags must be in attributes but NOT in nums
+      IF (ANY(attributes==list(i).AND..NOT.ANY(nums == list(i)))) j=j+1
+    ENDDO
+    ! No "valid" flags -> returns old csi
+    IF (j == 0) THEN ; ncsi = csi ; RETURN ; ENDIF
+    ni = SIZE(nums) ; no = j + ni
+    ALLOCATE(zlist(no)) ; zlist(1:ni) = nums(:) ; j = ni
+    DO i=1,SIZE(list) 
+      ! new flags must be in attributes but NOT in nums
+      IF (ANY(attributes==list(i).AND..NOT.ANY(nums == list(i)))) THEN
+        j=j+1 ; zlist(j) = list(i)
+      ENDIF 
+    ENDDO
+    DEALLOCATE(nums)
+    ! 2) Builds new csi
+    !    Here we explictly set the first flag to 0 (i.e. reset attributes)...
+    ncsi = CHAR(27)//"[0;"
+    DO i=1,no
+      ! ... So we get rid of all "0" flag in the list 
+      IF (zlist(i) /= 0) THEN
+        tmp = to_string(zlist(i))
+        IF (LEN_TRIM(tmp) == 0) THEN
+          ncsi = csi ; RETURN
+        ENDIF
+        ncsi = ncsi//tmp
+        IF (i /= no) ncsi = ncsi//";"
+      ENDIF
+    ENDDO
+    ncsi = ncsi//"m" 
+  END FUNCTION str_add_to_csi
+
+  FUNCTION str_del_from_csi(csi,list) RESULT(ncsi)
+    !! Remove a list of codes from the input csi string
+    !!
+    !! The method removes all the csi codes given in __list__ that are known by the
+    !! module and already present in the input csi.
+    CHARACTER(len=*), INTENT(in)      :: csi
+      !! An intrinsic Fortran string with the input csi. It __must__ begin with "<ESC>[" and ends with "m".
+    INTEGER, INTENT(in), DIMENSION(:) :: list
+      !! A vector of integers with the csi code to remove. Each value of __list__ should be one of 
+      !! [[string_op(module):attributes(variable)]] values. All unknown values are filtered out.
+    CHARACTER(len=:), ALLOCATABLE :: ncsi 
+      !! A new csi string or the input __csi__ if some "errors" occured (the input csi could not 
+      !! be tokenized or none of __list__ values are left after filtering).
+    LOGICAL                                            :: ok
+    CHARACTER(len=LEN(csi)), DIMENSION(:), ALLOCATABLE :: tks
+    CHARACTER(len=:), ALLOCATABLE                      :: tmp
+    INTEGER, DIMENSION(:), ALLOCATABLE                 :: nums
+    INTEGER                                            :: i
+    ncsi = csi(3:len(csi)-1) 
+    ok = tokenize(ncsi,tks,"; ",merge=.true.) 
+    IF (.NOT.from_string(tks,nums)) THEN 
+      ncsi = csi 
+      RETURN 
+    ENDIF
+    DEALLOCATE(tks)
+    tmp=""
+    DO i=1, SIZE(nums)
+      IF (ALL(nums(i) /= list).AND.nums(i) /= 0) THEN
+        ! no need to check for to_string status : it is always ok !
+        tmp=tmp//to_string(nums(i))//";"
+      ENDIF
+    ENDDO
+    IF (LEN_TRIM(tmp) /= 0) THEN
+      ncsi=CHAR(27)//"[0;"//tmp(1:LEN(tmp)-1)//"m"
+    ELSE
+      ncsi=CHAR(27)//"[0m"
+    ENDIF
+  END FUNCTION str_del_from_csi
+
+  FUNCTION str_index_of_csi(str,length) RESULT(pos)
+    !! Get the position of the first known csi in string
+    !!
+    !! The method searches for the first known csi in string. The csi must contain known codes 
+    !! (i.e. values of [[string_op(module):attributes(variable)]]).
+    CHARACTER(len=*), INTENT(in) :: str    !! A string to search in
+    INTEGER, INTENT(out)         :: length !! Length of the csi in the string
+    INTEGER                      :: pos    !! Position of the first csi found. It is set to 0 if no csi has been found.
+    INTEGER :: iesc,im
+    pos = 0 ; length = 0
+    ! we need at least 4 chars to create a csi
+    IF (LEN_TRIM(str) < 4) RETURN 
+    iesc = INDEX(str,CHAR(27))
+    IF (iesc == 0) RETURN
+    ! search for m
+    im = INDEX(str(iesc:),"m")
+    ! no m in the string after ESC --> copy string (INCLUDING ESC) and leave
+    IF (im == 0) RETURN
+    IF (.NOT.is_csi(str(iesc:iesc+im-1))) RETURN
+    pos = iesc ; length = im
+  END FUNCTION str_index_of_csi
+
+  ! String conversion functions
+  ! ---------------------------
+
+  FUNCTION str2int_sc(str, value) RESULT(ret)
+    !! Convert string value to integer value (scalar)
+    CHARACTER(len=*), INTENT(in) :: str   !! String to convert
+    INTEGER, INTENT(out)         :: value !! Output value
+    LOGICAL :: ret                        !! Return status (.true. on success)
+    CHARACTER(len=:), ALLOCATABLE :: zs
+    ret = .true. ; zs = remove_quotes(str)
+    IF (string_is(zs) /= st_integer) THEN
+      ret = .false.
+    ELSE
+      READ(zs, *) value
+    ENDIF
+    RETURN
+  END FUNCTION str2int_sc
+
+  FUNCTION str2log_sc(str, value) RESULT(ret)
+    !! Convert string value to logical value (scalar)
+    CHARACTER(len=*), INTENT(in) :: str   !! String to convert
+    LOGICAL, INTENT(out)         :: value !! Output value
+    LOGICAL :: ret                        !! Return status (.true. on success)
+    CHARACTER(len=:), ALLOCATABLE :: zs
+    integer :: r
+    ret = .true. ; zs = remove_quotes(str)
+    r = string_is(zs)
+    IF (string_is(zs) /= st_logical) THEN
+      ret = .false.
+    ELSE
+      READ(zs, *) value
+    ENDIF
+    RETURN
+  END FUNCTION str2log_sc
+
+  FUNCTION str2real_sc(str, value) RESULT(ret)
+    !! Convert string value to simple precision floating precision value (scalar)
+    CHARACTER(len=*), INTENT(in) :: str   !! String to convert
+    REAL(kind=4), INTENT(out)    :: value !! Output value
+    LOGICAL :: ret                        !! Return status (.true. on success)
+    CHARACTER(len=:), ALLOCATABLE :: zs
+    ret = .true.; zs = remove_quotes(str)
+    IF (string_is(zs) < st_integer) THEN
+      ret = .false.
+    ELSE
+      READ(zs, *) value
+    ENDIF
+    RETURN
+  END FUNCTION str2real_sc
+
+  FUNCTION str2dble_sc(str, value) RESULT(ret)
+    !! Convert string value to double precision floating precision value (scalar)
+    CHARACTER(len=*), INTENT(in) :: str   !! String to convert
+    REAL(kind=8), INTENT(out)    :: value !! Output value
+    LOGICAL :: ret                        !! Return status (.true. on success)
+    CHARACTER(len=:), ALLOCATABLE :: zs
+    ret = .true. ; zs = remove_quotes(str)
+    IF (string_is(zs) < st_integer) THEN
+      ret = .false.
+    ELSE
+      READ(zs, *) value
+    ENDIF
+    RETURN
+  END FUNCTION str2dble_sc
+
+  FUNCTION str2cplx_sc(str, value) RESULT(ret)
+    !! Convert string value to complex value (scalar)
+    CHARACTER(len=*), INTENT(in) :: str   !! String to convert
+    COMPLEX(kind=4), INTENT(out) :: value !! Output value
+    LOGICAL :: ret                        !! Return status (.true. on success)
+    ! - LOCAL 
+    CHARACTER(len=:), ALLOCATABLE :: zs
+    ret = .true. ; zs = remove_quotes(str)
+    IF (string_is(zs) /= st_complex) THEN
+      ret = .false.
+    ELSE
+      READ(zs, *) value
+    ENDIF
+    RETURN
+  END FUNCTION str2cplx_sc
+
+  FUNCTION str2int_ve(str, value) RESULT(ret)
+    !! Convert strings values to integer values (vector)
+    CHARACTER(len=*), INTENT(in), DIMENSION(:)      :: str   !! Vector of strings to convert
+    INTEGER, INTENT(out), DIMENSION(:), ALLOCATABLE :: value !! Vector of output values
+    LOGICAL :: ret                                           !! Return status (.true. on success)
+    INTEGER                       :: i,ns
+    CHARACTER(len=:), ALLOCATABLE :: zs
+    ret = .true. ; ns = SIZE(str) ; ALLOCATE(value(ns))
+    DO i=1,ns
+      zs = remove_quotes(str(i))
+      IF (string_is(zs) /= st_integer) THEN
+        ret = .false. ; DEALLOCATE(value) ; RETURN
+      ELSE
+        READ(zs, *) value(i)
+      ENDIF
+    ENDDO
+    RETURN
+  END FUNCTION str2int_ve
+
+  FUNCTION str2log_ve(str, value) RESULT(ret)
+    !! Convert strings values to logical values (vector)
+    CHARACTER(len=*), INTENT(in), DIMENSION(:)      :: str   !! Vector of strings to convert
+    LOGICAL, INTENT(out), DIMENSION(:), ALLOCATABLE :: value !! Vector of output values
+    LOGICAL :: ret                                           !! Return status (.true. on success)
+    INTEGER                       :: i,ns
+    CHARACTER(len=:), ALLOCATABLE :: zs
+    ret = .true. ; ns = SIZE(str) ; ALLOCATE(value(ns))
+    DO i=1,ns
+      zs = remove_quotes(str(i))
+      IF (string_is(zs) /= st_logical) THEN
+        ret = .false. ; DEALLOCATE(value) ; RETURN
+      ELSE
+        READ(zs, *) value(i)
+      ENDIF
+    ENDDO
+    RETURN
+  END FUNCTION str2log_ve
+
+  FUNCTION str2real_ve(str, value) RESULT(ret)
+    !! Convert strings values to simple precision floating point values (vector)
+    CHARACTER(len=*), INTENT(in), DIMENSION(:)           :: str   !! Vector of strings to convert
+    REAL(kind=4), INTENT(out), DIMENSION(:), ALLOCATABLE :: value !! Vector of output values
+    LOGICAL :: ret                                                !! Return status (.true. on success) 
+    INTEGER                       :: i,ns
+    CHARACTER(len=:), ALLOCATABLE :: zs
+    ret = .true. ; ns = SIZE(str) ; ALLOCATE(value(ns))
+    DO i=1,ns
+      IF (string_is(zs) < st_integer) THEN
+        ret = .false. ; DEALLOCATE(value) ; RETURN
+      ELSE
+        READ(zs, *) value(i)
+      ENDIF
+    ENDDO
+    RETURN
+  END FUNCTION str2real_ve
+
+  FUNCTION str2dble_ve(str, value) RESULT(ret)
+    !! Convert strings values to double precision floating point values (vector)
+    CHARACTER(len=*), INTENT(in), DIMENSION(:)           :: str   !! Vector of strings to convert
+    REAL(kind=8), INTENT(out), DIMENSION(:), ALLOCATABLE :: value !! Vector of output values
+    LOGICAL :: ret                                                !! Return status (.true. on success)
+    INTEGER                       :: i,ns
+    CHARACTER(len=:), ALLOCATABLE :: zs
+    ret = .true. ; ns = SIZE(str) ; ALLOCATE(value(ns))
+    DO i=1,ns
+      zs = remove_quotes(str(i))
+      IF (string_is(zs) < st_integer) THEN
+        ret = .false. ; DEALLOCATE(value) ; RETURN
+      ELSE
+        READ(zs, *) value(i)
+      ENDIF
+    ENDDO
+    RETURN
+  END FUNCTION str2dble_ve
+
+  FUNCTION str2cplx_ve(str, value) RESULT(ret)
+    !! Convert strings values to complex values (vector)
+    CHARACTER(len=*), INTENT(in), DIMENSION(:)              :: str   !! Vector of strings to convert
+    COMPLEX(kind=4), INTENT(out), DIMENSION(:), ALLOCATABLE :: value !! Vector of output values
+    LOGICAL :: ret                                                   !! Return status (.true. on success)
+    INTEGER                       :: i,ns
+    CHARACTER(len=:), ALLOCATABLE :: zs
+    ret = .true. ; ns = SIZE(str) ; ALLOCATE(value(ns))
+    DO i=1,ns
+      zs = remove_quotes(str(i))
+      IF (string_is(zs) /= st_complex) THEN
+        ret = .false. ; DEALLOCATE(value) ; RETURN
+      ELSE
+        READ(zs, *) value(i)
+      ENDIF
+    ENDDO
+    RETURN
+  END FUNCTION str2cplx_ve
+
+  FUNCTION int2str_as(value) RESULT(str)
+    !! Convert an integer value to string (auto format / string result) 
+    INTEGER, INTENT(in)           :: value !! Value to convert
+    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
+    INTEGER :: err
+    ALLOCATE(CHARACTER(len=DIGITS(value)) :: str)
+    WRITE(str,*,iostat=err) value
+    str = TRIM(ADJUSTL(str))
+    IF (err /= 0) str = '' 
+    RETURN
+  END FUNCTION int2str_as
+
+  FUNCTION log2str_as(value) RESULT(str)
+    !! Convert a logical value to string (auto format / string result) 
+    LOGICAL, INTENT(in)           :: value !! Value to convert
+    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
+    INTEGER :: err
+    ALLOCATE(CHARACTER(len=2) :: str)
+    WRITE(str, *, IOSTAT = err) value
+    str=TRIM(ADJUSTL(str))
+    IF (err /= 0) str = ''
+    RETURN
+  END FUNCTION log2str_as
+
+  FUNCTION real2str_as(value) RESULT(str)
+    !! Convert a simple precision floating point value to string (auto format / string result) 
+    REAL(kind=4), INTENT(in)      :: value !! Value to convert
+    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
+    INTEGER :: err
+    ALLOCATE(CHARACTER(len=DIGITS(value)) ::str)
+    WRITE(str,*, IOSTAT = err) value
+    str=TRIM(ADJUSTL(str))
+    IF (err /= 0)  str = '' 
+    RETURN
+  END FUNCTION real2str_as
+
+  FUNCTION dble2str_as(value) RESULT(str)
+    !! Convert a double precision floating point value to string (auto format / string result) 
+    REAL(kind=8), INTENT(in)      :: value !! Value to convert
+    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
+    INTEGER :: err
+    ALLOCATE(CHARACTER(len=DIGITS(value)) ::str)
+    WRITE(str,*, IOSTAT = err) value
+    str=TRIM(ADJUSTL(str))
+    IF (err /= 0) str = '' 
+    RETURN
+  END FUNCTION dble2str_as
+
+  FUNCTION cplx2str_as(value) RESULT(str)
+    !! Convert a complex value to string (auto format / string result) 
+    COMPLEX(kind=4), INTENT(in)   :: value !! Value to convert
+    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
+    INTEGER :: err,sl
+    sl = DIGITS(REAL(value))*2+3
+    ALLOCATE(CHARACTER(len=sl) :: str)
+    WRITE(str, *, IOSTAT = err) value
+    str = TRIM(ADJUSTL(str))
+    IF (err /= 0) str = '' 
+    RETURN
+  END FUNCTION cplx2str_as
+
+  FUNCTION dcplx2str_as(value) RESULT(str)
+    !! Convert a complex value to string (auto format / string result) 
+    COMPLEX(kind=8), INTENT(in)   :: value !! Value to convert
+    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
+    INTEGER :: err,sl
+    sl = DIGITS(REAL(value))*2+3
+    ALLOCATE(CHARACTER(len=sl) :: str)
+    WRITE(str, *, IOSTAT = err) value
+    str = TRIM(ADJUSTL(str))
+    IF (err /= 0) str = '' 
+    RETURN
+  END FUNCTION dcplx2str_as
+
+  FUNCTION int2str_fs(value, fmt) RESULT(str)
+    !! Convert an integer value to string (user format / string result) 
+    INTEGER, INTENT(in)           :: value !! Value to convert
+    CHARACTER(len=*), INTENT(in)  :: fmt   !! String format
+    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
+    INTEGER :: err
+    ALLOCATE(CHARACTER(len=st_slen) :: str)
+    WRITE(str, '('//fmt//')', IOSTAT = err) value
+    str = TRIM(ADJUSTL(str))
+    IF (err /= 0) str = '' 
+    RETURN
+  END FUNCTION int2str_fs
+
+  FUNCTION log2str_fs(value, fmt) RESULT(str)
+    !! Convert a logical value to string (user format / string result) 
+    LOGICAL, INTENT(in)           :: value !! Value to convert
+    CHARACTER(len=*), INTENT(in)  :: fmt   !! String format
+    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
+    INTEGER :: err
+    ALLOCATE(CHARACTER(len=st_slen) :: str)
+    WRITE(str, '('//fmt//')', IOSTAT = err) value
+    str=TRIM(ADJUSTL(str))
+    IF (err /= 0) str = '' 
+    RETURN
+  END FUNCTION log2str_fs
+
+  FUNCTION real2str_fs(value, fmt) RESULT(str)
+    !! Convert a simple precision floating point value to string (user format / string result) 
+    REAL(kind=4), INTENT(in)      :: value !! Value to convert
+    CHARACTER(len=*), INTENT(in)  :: fmt   !! String format
+    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
+    INTEGER :: err
+    ALLOCATE(CHARACTER(len=st_slen) :: str)
+    WRITE(str, '('//fmt//')', IOSTAT = err) value
+    str = TRIM(ADJUSTL(str))
+    IF (err /= 0) str = '' 
+    RETURN
+  END FUNCTION real2str_fs
+
+  FUNCTION dble2str_fs(value, fmt) RESULT(str)
+    !! Convert a double precision floating point value to string (user format / string result) 
+    REAL(kind=8), INTENT(in)      :: value !! Value to convert
+    CHARACTER(len=*), INTENT(in)  :: fmt   !! String format
+    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
+    INTEGER :: err
+    ALLOCATE(CHARACTER(len=st_slen) :: str)
+    WRITE(str, '('//fmt//')', IOSTAT = err) value
+    str = TRIM(ADJUSTL(str))
+    IF (err /= 0) str = '' 
+    RETURN
+  END FUNCTION dble2str_fs
+
+  FUNCTION cplx2str_fs(value, fmt) RESULT(str)
+    !! Convert a complex value to string (user format / string result) 
+    COMPLEX(kind=4), INTENT(in)   :: value !! Value to convert
+    CHARACTER(len=*), INTENT(in)  :: fmt   !! String format
+    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
+    INTEGER :: err
+    ALLOCATE(CHARACTER(len=st_slen) :: str)
+    WRITE(str, '('//fmt//')', IOSTAT = err) value
+    str = TRIM(ADJUSTL(str))
+    IF (err /= 0) str = '' 
+    RETURN
+  END FUNCTION cplx2str_fs
+
+  FUNCTION dcplx2str_fs(value, fmt) RESULT(str)
+    !! Convert a complex value to string (user format / string result) 
+    COMPLEX(kind=8), INTENT(in)   :: value !! Value to convert
+    CHARACTER(len=*), INTENT(in)  :: fmt   !! String format
+    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
+    INTEGER :: err
+    ALLOCATE(CHARACTER(len=st_slen) :: str)
+    WRITE(str, '('//fmt//')', IOSTAT = err) value
+    str = TRIM(ADJUSTL(str))
+    IF (err /= 0) str = '' 
+    RETURN
+  END FUNCTION dcplx2str_fs
+
+  ! Extended strings features
+  ! ---------------------------
+
+  FUNCTION fis_cat_int(str1,int2) RESULT(str)
+    !! Concatenate a fortran intrinsic string with a integer.
+    CHARACTER(len=*), INTENT(in)  :: str1 !! String to concatenate
+    INTEGER, INTENT(in)           :: int2 !! Integer to concatenate
+    CHARACTER(len=:), ALLOCATABLE :: str  !! Output string
+    ALLOCATE(CHARACTER(len=DIGITS(int2)) :: str) 
+    WRITE(str,*) int2 ; str = TRIM(ADJUSTL(str))
+    IF (LEN(str1) /= 0) str = str1//str
+    RETURN
+  END FUNCTION fis_cat_int
+
+    !! @param[in] int2 An integer to concatenate
+    !! @param[in] str1 A string to concatenate
+    !! @return An allocatable string with the concatenation of input values.
+  FUNCTION fis_cat_int_inv(int2,str1) RESULT(str)
+    !! Concatenate a fortran intrinsic string with a integer (reversed).
+    INTEGER, INTENT(in)           :: int2 !! Integer to concatenate 
+    CHARACTER(len=*), INTENT(in)  :: str1 !! String to concatenate
+    CHARACTER(len=:), ALLOCATABLE :: str  !! Output string
+    ALLOCATE(CHARACTER(len=DIGITS(int2)) :: str) 
+    WRITE(str,*) int2 ; str = TRIM(ADJUSTL(str))
+    IF (LEN(str1) /= 0) str = str//str1
+    RETURN
+  END FUNCTION fis_cat_int_inv
+
+  FUNCTION fis_cat_bool(str1,bool2) RESULT(str)
+    !! Concatenate a string with a logical
+    CHARACTER(len=*), INTENT(in)  :: str1  !! String to concatenate
+    LOGICAL, INTENT(in)           :: bool2 !! Logical to concatenate
+    CHARACTER(len=:), ALLOCATABLE :: str   !! Output string
+    CHARACTER(len=2) ::tmp
+    WRITE(tmp,*) bool2 
+    str=TRIM(ADJUSTL(tmp))
+    IF (LEN(str1) /= 0) str = str1//str
+    RETURN
+  END FUNCTION fis_cat_bool
+
+  FUNCTION fis_cat_bool_inv(bool2,str1) RESULT(str)
+    !! Concatenate a string with a logical (reversed)
+    LOGICAL, INTENT(in)           :: bool2 !! Logical to concatenate
+    CHARACTER(len=*), INTENT(in)  :: str1    !! String to concatenate
+    CHARACTER(len=:), ALLOCATABLE :: str     !! Output string
+    CHARACTER(len=2) ::tmp 
+    WRITE(tmp,*) bool2 
+    str = TRIM(ADJUSTL(tmp))
+    IF (LEN(str1) /= 0) str = str//str1
+    RETURN
+  END FUNCTION fis_cat_bool_inv
+
+  FUNCTION fis_cat_real(str1,real2) RESULT(str)
+    !! Concatenate a string with a real simple precision
+    CHARACTER(len=*), INTENT(in)  :: str1    !! String to concatenate
+    REAL(kind=4), INTENT(in)      :: real2 !! Simple precision real to concatenate
+    CHARACTER(len=:), ALLOCATABLE :: str     !! Output string
+    ALLOCATE(CHARACTER(len=DIGITS(real2)) :: str) 
+    WRITE(str,*) real2 ; str = TRIM(ADJUSTL(str))
+    IF (LEN(str1) /= 0) str=str1//str 
+    RETURN
+  END FUNCTION fis_cat_real
+
+  FUNCTION fis_cat_real_inv(real2,str1) RESULT(str)
+    !! Concatenate a string with a real simple precision (reversed)
+    REAL(kind=4), INTENT(in)      :: real2 !! Simple precision real to concatenate
+    CHARACTER(len=*), INTENT(in)  :: str1  !! String to concatenate
+    CHARACTER(len=:), ALLOCATABLE :: str   !! Output string
+    ALLOCATE(CHARACTER(len=DIGITS(real2)) :: str) 
+    WRITE(str,*) real2  ; str = TRIM(ADJUSTL(str))
+    IF (LEN(str1) /= 0) str = str//str1
+    RETURN
+  END FUNCTION fis_cat_real_inv
+
+  FUNCTION fis_cat_double(str1,double2) RESULT(str)
+    !! Concatenate a string with a real double precision
+    CHARACTER(len=*), INTENT(in)  :: str1    !! String to concatenate
+    REAL(kind=8), INTENT(in)      :: double2 !! Double precision real to concatenate
+    CHARACTER(len=:), ALLOCATABLE :: str     !! Output string
+    ALLOCATE(CHARACTER(len=DIGITS(double2)) :: str) 
+    WRITE(str,*) double2 ; str = TRIM(ADJUSTL(str))
+    IF (LEN(str1) /= 0) str=str1//str 
+    RETURN
+  END FUNCTION fis_cat_double
+
+  FUNCTION fis_cat_double_inv(double2,str1) RESULT(str)
+    !! Concatenate a string with a real double precision (reversed)
+    REAL(kind=8), INTENT(in)      :: double2 !! Double precision real to concatenate
+    CHARACTER(len=*), INTENT(in)  :: str1    !! String to concatenate
+    CHARACTER(len=:), ALLOCATABLE :: str     !! Output string
+    ALLOCATE(CHARACTER(len=DIGITS(double2)) :: str) 
+    WRITE(str,*) double2  ; str = TRIM(ADJUSTL(str))
+    IF (LEN(str1) /= 0) str = str//str1
+    RETURN
+  END FUNCTION fis_cat_double_inv
+
+  FUNCTION fis_cat_cplx(str1,cplx2) RESULT(str)
+    !! Concatenate a string with a complex 
+    CHARACTER(len=*), INTENT(in)  :: str1  !! String to concatenate 
+    COMPLEX(kind=4), INTENT(in)   :: cplx2 !! Complex value to concatenate 
+    CHARACTER(len=:), ALLOCATABLE :: str   !! Output string
+    INTEGER :: sl
+    sl = DIGITS(REAL(cplx2))*2+3
+    ALLOCATE(CHARACTER(len=sl) :: str)
+    WRITE(str,*) cplx2 ; str = TRIM(ADJUSTL(str))
+    IF (LEN(str1) /= 0) str = str//str1
+    RETURN
+  END FUNCTION fis_cat_cplx
+
+  FUNCTION fis_cat_cplx_inv(cplx2,str1) RESULT(str)
+    !! Concatenate a string with a complex (reversed)
+    COMPLEX(kind=4), INTENT(in)   :: cplx2 !! Complex value to concatenate 
+    CHARACTER(len=*), INTENT(in)  :: str1  !! String to concatenate 
+    CHARACTER(len=:), ALLOCATABLE :: str   !! Output string
+    INTEGER :: sl
+    sl = DIGITS(REAL(cplx2))*2+3
+    ALLOCATE(CHARACTER(len=sl) :: str)
+    WRITE(str,*) cplx2
+    str = TRIM(ADJUSTL(str))
+    IF (LEN(str1) /= 0) str = str//str1
+    RETURN
+  END FUNCTION fis_cat_cplx_inv
+
+  FUNCTION fis_cat_dcplx(str1,dcplx2) RESULT(str)
+    !! Concatenate a string with a double precision complex 
+    CHARACTER(len=*), INTENT(in)  :: str1   !! String to concatenate
+    COMPLEX(kind=8), INTENT(in)   :: dcplx2 !! Complex value to concatenate 
+    CHARACTER(len=:), ALLOCATABLE :: str    !! Output string
+    INTEGER :: sl
+    sl = DIGITS(REAL(dcplx2))*2+3
+    ALLOCATE(CHARACTER(len=sl) :: str)
+    WRITE(str,*) dcplx2 ; str = TRIM(ADJUSTL(str))
+    IF (LEN(str1) /= 0) str = str//str1
+    RETURN
+  END FUNCTION fis_cat_dcplx
+
+  FUNCTION fis_cat_dcplx_inv(dcplx2,str1) RESULT(str)
+    !! Concatenate a string with a double precision complex (reversed)
+    COMPLEX(kind=8), INTENT(in)   :: dcplx2 !! Complex value to concatenate 
+    CHARACTER(len=*), INTENT(in)  :: str1   !! string to concatenate
+    CHARACTER(len=:), ALLOCATABLE :: str    !! Output string
+    INTEGER :: sl
+    sl = DIGITS(REAL(dcplx2))*2+3
+    ALLOCATE(CHARACTER(len=sl) :: str)
+    WRITE(str,*) dcplx2
+    str = TRIM(ADJUSTL(str))
+    IF (LEN(str1) /= 0) str = str//str1
+    RETURN
+  END FUNCTION fis_cat_dcplx_inv
+
+  SUBROUTINE fis_affect_int(str,int)
+    !! Assignment subroutine (using intrinsic integer) 
+    CHARACTER(len=:), INTENT(out), ALLOCATABLE :: str !! Output string to be assigned
+    INTEGER, INTENT(in)                        :: int !! Input value to assign 
+    str = fis_cat_int('',int)
+  END SUBROUTINE fis_affect_int
+
+  SUBROUTINE fis_affect_bool(str,bool)
+    !! Assignment subroutine (using intrinsic logical) 
+    CHARACTER(len=:), INTENT(out), ALLOCATABLE :: str  !! Output string to be assigned
+    LOGICAL, INTENT(in)                        :: bool !! Input value to assign
+    str = fis_cat_bool('',bool)
+  END SUBROUTINE fis_affect_bool
+
+  SUBROUTINE fis_affect_real(str,float)
+    !! Assignment subroutine (using intrinsic real) 
+    CHARACTER(len=:), INTENT(out), ALLOCATABLE :: str   !! Output string to be assigned
+    REAL(kind=4), INTENT(in)                   :: float !! Input value to assign
+    str = fis_cat_real('',float)
+  END SUBROUTINE fis_affect_real
+
+  SUBROUTINE fis_affect_double(str,double)
+    !! Assignment subroutine (using intrinsic real(kind=8)) 
+    CHARACTER(len=:), INTENT(out), ALLOCATABLE :: str    !! Output string to be assigned
+    REAL(kind=8), INTENT(in)                   :: double !! Input value to assign 
+    str = fis_cat_double('',double)
+  END SUBROUTINE fis_affect_double
+
+  SUBROUTINE fis_affect_cplx(str,cplx)
+    !! Assignment subroutine (using intrinsic complex) 
+    CHARACTER(len=:), INTENT(out), ALLOCATABLE :: str  !! Output string to be assigned
+    COMPLEX(kind=4), INTENT(in)                :: cplx !! Input value to assign
+    str = fis_cat_cplx('',cplx)
+  END SUBROUTINE fis_affect_cplx
+
+  SUBROUTINE fis_affect_dcplx(str,dcplx)
+    !! Assignment subroutine (using intrinsic complex(kind=8)) 
+    CHARACTER(len=:), INTENT(out), ALLOCATABLE :: str   !! Output string to be assigned
+    COMPLEX(kind=8), INTENT(in)                :: dcplx !! Input value to assign
+    str = fis_cat_dcplx('',dcplx)
+  END SUBROUTINE fis_affect_dcplx
+
+  FUNCTION get_attrs_indexes(flags) RESULT(codes)
+    !! Convert a list of csi flags into a csi codes.
+    !!
+    !! Only know CSI codes are returned. If no known CSI are found the outputput vector is
+    !! allocated with 0 elements.
+    CHARACTER(len=2), DIMENSION(:), INTENT(in) :: flags !! CSI attributes flags 
+    INTEGER, DIMENSION(:), ALLOCATABLE :: codes         !! CSI codes.
+    INTEGER :: i,j,n
+    n = 0
+    ALLOCATE(codes(SIZE(flags)))
+    codes(:) = -1
+    DO i = 1, SIZE(flags)
+      DO j = 1, SIZE(attributes)
+        IF (to_lower(flags(i)) == csis(j)) THEN
+          n = n + 1
+          codes(n) = attributes(j)
+          EXIT
+        ENDIF
+      ENDDO
+    ENDDO
+    IF (n > 0) THEN
+      codes = codes(1:n)
+    ELSE
+      DEALLOCATE(codes)
+      ALLOCATE(codes(0))
+    ENDIF
+  END FUNCTION get_attrs_indexes
+
+  FUNCTION fancy_fstr(value,flags,fmt) RESULT(output)
+    !! Compute a fancy string from the given (fortran intrinsic) string.
+    CHARACTER(len=*), INTENT(in)               :: value  !! String object reference 
+    CHARACTER(len=2), DIMENSION(:), INTENT(in) :: flags  !! CSI attributes flags
+    CHARACTER(len=*), INTENT(in), OPTIONAL     :: fmt    !! Optional format (unused for this overload)
+    CHARACTER(len=:), ALLOCATABLE              :: output !! Output fortran instrinsic string
+    INTEGER, DIMENSION(:), ALLOCATABLE :: codes
+    codes = get_attrs_indexes(flags) 
+    IF (SIZE(codes) == 0) THEN
+      output = value ; RETURN
+    ELSE
+      output = add_csi(value,codes)
+    ENDIF
+  END FUNCTION fancy_fstr
+
+  FUNCTION fancy_int(value,flags,fmt) RESULT(output)
+    !! Compute a fancy string from the given integer value.
+    INTEGER, INTENT(in)                        :: value  !! String object reference 
+    CHARACTER(len=2), DIMENSION(:), INTENT(in) :: flags  !! CSI attributes flags
+    CHARACTER(len=*), INTENT(in), OPTIONAL     :: fmt    !! Optional format. If given it must be a valid Fortran format.
+    CHARACTER(len=:), ALLOCATABLE              :: output !! Output fortran instrinsic string
+    INTEGER, DIMENSION(:), ALLOCATABLE :: codes
+    CHARACTER(len=:), ALLOCATABLE      :: tmp 
+    codes = get_attrs_indexes(flags) 
+    IF (PRESENT(fmt)) THEN ; tmp = to_string(value,fmt) ; ELSE ; tmp = to_string(value) ; ENDIF
+    IF (SIZE(codes) /= 0) THEN ; output = add_csi(tmp,codes) ; ELSE ; output = tmp ; ENDIF
+  END FUNCTION fancy_int
+
+  FUNCTION fancy_bool(value,flags,fmt) RESULT(output)
+    !! Compute a fancy string from the given logical value.
+    LOGICAL, INTENT(in)                        :: value  !! String object reference 
+    CHARACTER(len=2), DIMENSION(:), INTENT(in) :: flags  !! CSI attributes flags
+    CHARACTER(len=*), INTENT(in), OPTIONAL     :: fmt    !! Optional format. If given it must be a valid Fortran format.
+    CHARACTER(len=:), ALLOCATABLE              :: output !! Output fortran instrinsic string
+    INTEGER, DIMENSION(:), ALLOCATABLE :: codes
+    CHARACTER(len=:), ALLOCATABLE      :: tmp 
+    codes = get_attrs_indexes(flags) 
+    IF (PRESENT(fmt)) THEN ; tmp = to_string(value,fmt) ; ELSE ; tmp = to_string(value) ; ENDIF
+    IF (SIZE(codes) /= 0) THEN ; output = add_csi(tmp,codes) ; ELSE ; output = tmp ; ENDIF
+  END FUNCTION fancy_bool
+
+  FUNCTION fancy_real(value,flags,fmt) RESULT(output)
+    !! Compute a fancy string from the given real value (simple precision).
+    REAL(kind=4), INTENT(in)                   :: value  !! String object reference 
+    CHARACTER(len=2), DIMENSION(:), INTENT(in) :: flags  !! CSI attributes flags
+    CHARACTER(len=*), INTENT(in), OPTIONAL     :: fmt    !! Optional format. If given it must be a valid Fortran format.
+    CHARACTER(len=:), ALLOCATABLE              :: output !! Output fortran instrinsic string
+    INTEGER, DIMENSION(:), ALLOCATABLE :: codes
+    CHARACTER(len=:), ALLOCATABLE      :: tmp 
+    codes = get_attrs_indexes(flags) 
+    IF (PRESENT(fmt)) THEN ; tmp = to_string(value,fmt) ; ELSE ; tmp = to_string(value) ; ENDIF
+    IF (SIZE(codes) /= 0) THEN ; output = add_csi(tmp,codes) ; ELSE ; output = tmp ; ENDIF
+  END FUNCTION fancy_real 
+
+  FUNCTION fancy_double(value,flags,fmt) RESULT(output)
+    !! Compute a fancy string from the given real value (double precision).
+    REAL(kind=8), INTENT(in)                   :: value  !! String object reference 
+    CHARACTER(len=2), DIMENSION(:), INTENT(in) :: flags  !! CSI attributes flags
+    CHARACTER(len=*), INTENT(in), OPTIONAL     :: fmt    !! Optional format. If given it must be a valid Fortran format.
+    CHARACTER(len=:), ALLOCATABLE              :: output !! Output fortran instrinsic string
+    INTEGER, DIMENSION(:), ALLOCATABLE :: codes
+    CHARACTER(len=:), ALLOCATABLE      :: tmp 
+    codes = get_attrs_indexes(flags) 
+    IF (PRESENT(fmt)) THEN ; tmp = to_string(value,fmt) ; ELSE ; tmp = to_string(value) ; ENDIF
+    IF (SIZE(codes) /= 0) THEN ; output = add_csi(tmp,codes) ; ELSE ; output = tmp ; ENDIF
+  END FUNCTION fancy_double
+
+  FUNCTION fancy_cplx(value,flags,fmt) RESULT(output)
+    !! Compute a fancy string from the given complex value (simple precision).
+    COMPLEX(kind=4), INTENT(in)                :: value  !! String object reference 
+    CHARACTER(len=2), DIMENSION(:), INTENT(in) :: flags  !! CSI attributes flags
+    CHARACTER(len=*), INTENT(in), OPTIONAL     :: fmt    !! Optional format. If given it must be a valid Fortran format.
+    CHARACTER(len=:), ALLOCATABLE              :: output !! Output fortran instrinsic string
+    INTEGER, DIMENSION(:), ALLOCATABLE :: codes
+    CHARACTER(len=:), ALLOCATABLE      :: tmp 
+    codes = get_attrs_indexes(flags) 
+    IF (PRESENT(fmt)) THEN ; tmp = to_string(value,fmt) ; ELSE ; tmp = to_string(value) ; ENDIF
+    IF (SIZE(codes) /= 0) THEN ; output = add_csi(tmp,codes) ; ELSE ; output = tmp ; ENDIF
+  END FUNCTION fancy_cplx
+  
+  FUNCTION fancy_dcplx(value,flags,fmt) RESULT(output)
+    !! Compute a fancy string from the given complex value (double precision).
+    COMPLEX(kind=8), INTENT(in)                :: value  !! String object reference 
+    CHARACTER(len=2), DIMENSION(:), INTENT(in) :: flags  !! CSI attributes flags
+    CHARACTER(len=*), INTENT(in), OPTIONAL     :: fmt    !! Optional format. If given it must be a valid Fortran format.
+    CHARACTER(len=:), ALLOCATABLE              :: output !! Output fortran instrinsic string
+    INTEGER, DIMENSION(:), ALLOCATABLE :: codes
+    CHARACTER(len=:), ALLOCATABLE      :: tmp 
+    codes = get_attrs_indexes(flags) 
+    IF (PRESENT(fmt)) THEN ; tmp = to_string(value,fmt) ; ELSE ; tmp = to_string(value) ; ENDIF
+    IF (SIZE(codes) /= 0) THEN ; output = add_csi(tmp,codes) ; ELSE ; output = tmp ; ENDIF
+  END FUNCTION fancy_dcplx
+
+END MODULE STRING_OP
Index: trunk/LMDZ.TITAN/libf/muphytitan/strings.F90
===================================================================
--- trunk/LMDZ.TITAN/libf/muphytitan/strings.F90	(revision 1896)
+++ 	(revision )
@@ -1,2416 +1,0 @@
-! Copyright Jérémie Burgalat (2017)
-! 
-! burgalat.jeremie@gmail.com
-! 
-! This software is a computer program whose purpose is to provide configuration 
-! file and command line arguments parsing features to Fortran programs.
-! 
-! This software is governed by the CeCILL-B license under French law and
-! abiding by the rules of distribution of free software.  You can  use, 
-! modify and/ or redistribute the software under the terms of the CeCILL-B
-! license as circulated by CEA, CNRS and INRIA at the following URL
-! "http://www.cecill.info". 
-! 
-! As a counterpart to the access to the source code and  rights to copy,
-! modify and redistribute granted by the license, users are provided only
-! with a limited warranty  and the software's author,  the holder of the
-! economic rights,  and the successive licensors  have only  limited
-! liability. 
-! 
-! In this respect, the user's attention is drawn to the risks associated
-! with loading,  using,  modifying and/or developing or reproducing the
-! software by the user in light of its specific status of free software,
-! that may mean  that it is complicated to manipulate,  and  that  also
-! therefore means  that it is reserved for developers  and  experienced
-! professionals having in-depth computer knowledge. Users are therefore
-! encouraged to load and test the software's suitability as regards their
-! requirements in conditions enabling the security of their systems and/or 
-! data to be ensured and,  more generally, to use and operate it in the 
-! same conditions as regards security. 
-! 
-! The fact that you are presently reading this means that you have had
-! knowledge of the CeCILL-B license and that you accept its terms.
-
-!! file: strings.F90
-!! summary: Strings manipulation source file
-!! author: burgalat
-!! date: 2017
-
-#include "defined.h"
-
-MODULE STRINGS
-  !! Fortran strings manipulation module
-  !! 
-  !! This module provides methods and objects to manipulate Fortran (allocatable) strings. It defines
-  !! a doubly linked-list of strings, [[strings(module):words(type)]] and several methods to format
-  !! strings or convert them in other intrinsic types. 
-  USE ERRORS
-  IMPLICIT NONE
-  
-  PRIVATE
-
-  PUBLIC ::  str2dble_sc,str2dble_ve,str2real_sc,str2real_ve
-  ! errors module (not used but propagated)
-  PUBLIC :: noerror,error, error_to_string,aborting
-  
-  ! misc module methods
-  PUBLIC :: to_string, from_string, string_is, remove_quotes, format_string,     &
-            format_paragraph, strip_newline, tokenize, str_length, str_endswith, &
-            str_startswith, str_to_lower, str_to_upper, str_add_attributes,      &
-            str_delete_attributes, str_reset_attributes, str_remove, str_replace
-
-  ! words object related methods
-  PUBLIC :: words_length, words_insert, words_append, words_prepend, words_get, &
-            words_set, words_get_max_width, words_get_total_width, words_pop,   &
-            words_remove, words_next, words_previous, words_reset,              &
-            words_valid, words_current, words_extend, words_reverse,            &
-            words_reversed, words_dump, words_to_string, words_to_vector,       &
-            new_words, words_clear
-
-  ! Operators
-  PUBLIC :: ASSIGNMENT(=), OPERATOR(/=), OPERATOR(==)
-
-  INTEGER, PUBLIC, PARAMETER :: st_string  = 1 !! String type ID
-  INTEGER, PUBLIC, PARAMETER :: st_logical = 2 !! Logical type ID
-  INTEGER, PUBLIC, PARAMETER :: st_complex = 3 !! Complex type ID
-  INTEGER, PUBLIC, PARAMETER :: st_integer = 4 !! Integer type ID
-  INTEGER, PUBLIC, PARAMETER :: st_real    = 5 !! Real type ID
-  
-  !> List of types names
-  CHARACTER(len=*), DIMENSION(5), PARAMETER, PUBLIC :: st_type_names = &
-  (/ 'string ', 'logical', 'complex', 'integer', 'real   '/)
-
-  INTEGER, PUBLIC, PARAMETER :: st_slen = SSLEN !! Maximum short string length
-  INTEGER, PUBLIC, PARAMETER :: st_llen = SLLEN !! Maximum long string length
-  
-  INTEGER, PUBLIC, PARAMETER :: FC_BLACK     = 30 !! Black foreground csi code
-  INTEGER, PUBLIC, PARAMETER :: FC_RED       = 31 !! Red foreground csi code
-  INTEGER, PUBLIC, PARAMETER :: FC_GREEN     = 32 !! Green foreground csi code
-  INTEGER, PUBLIC, PARAMETER :: FC_YELLOW    = 33 !! Yellow foreground csi code
-  INTEGER, PUBLIC, PARAMETER :: FC_BLUE      = 34 !! Blue foreground csi code
-  INTEGER, PUBLIC, PARAMETER :: FC_MAGENTA   = 35 !! Magenta foreground csi code
-  INTEGER, PUBLIC, PARAMETER :: FC_CYAN      = 36 !! Cyan foreground csi code
-  INTEGER, PUBLIC, PARAMETER :: FC_WHITE     = 37 !! White foreground csi code
-  INTEGER, PUBLIC, PARAMETER :: BG_BLACK     = 40 !! Black foreground csi code
-  INTEGER, PUBLIC, PARAMETER :: BG_RED       = 41 !! Black background csi code
-  INTEGER, PUBLIC, PARAMETER :: BG_GREEN     = 42 !! Green background csi code
-  INTEGER, PUBLIC, PARAMETER :: BG_YELLOW    = 43 !! Yellow background csi code
-  INTEGER, PUBLIC, PARAMETER :: BG_BLUE      = 44 !! Blue background csi code
-  INTEGER, PUBLIC, PARAMETER :: BG_MAGENTA   = 45 !! Magenta background csi code
-  INTEGER, PUBLIC, PARAMETER :: BG_CYAN      = 46 !! Cyan background csi code
-  INTEGER, PUBLIC, PARAMETER :: BG_WHITE     = 47 !! White background csi code
-  INTEGER, PUBLIC, PARAMETER :: ST_NORMAL    =  0 !! Normal (regular) attribute
-  INTEGER, PUBLIC, PARAMETER :: ST_BOLD      =  1 !! Bold (brighter) attribute
-  INTEGER, PUBLIC, PARAMETER :: ST_ITALIC    =  3 !! Italic attribute (sometimes reverse video or underline)
-  INTEGER, PUBLIC, PARAMETER :: ST_UNDERLINE =  4 !! Underline attribute
-  INTEGER, PUBLIC, PARAMETER :: ST_BLINK     =  5 !! Slow blink mode
-  !> List of all attributes in a vector
-  INTEGER, PUBLIC, PARAMETER, DIMENSION(21) :: attributes = [FC_BLACK,     &
-                                                             FC_RED,       &
-                                                             FC_GREEN,     & 
-                                                             FC_YELLOW,    &
-                                                             FC_BLUE,      &
-                                                             FC_MAGENTA,   &
-                                                             FC_CYAN,      &
-                                                             FC_WHITE,     &
-                                                             BG_BLACK,     &
-                                                             BG_RED,       &
-                                                             BG_GREEN,     &
-                                                             BG_YELLOW,    &
-                                                             BG_BLUE,      &
-                                                             BG_MAGENTA,   &
-                                                             BG_CYAN,      &
-                                                             BG_WHITE,     & 
-                                                             ST_NORMAL,    &
-                                                             ST_BOLD,      &
-                                                             ST_ITALIC,    &
-                                                             ST_UNDERLINE, &
-                                                             ST_BLINK      &
-                                                            ]     
-  
-  !> [[words(type)]] object assignement interface
-  INTERFACE ASSIGNMENT(=)
-    MODULE PROCEDURE ws_affect
-  END INTERFACE
-
-  
-  !> [[words(type)]] interface constructor
-  !!
-  !! The interface encapsulates the two constructors of the words object:
-  !!
-  !! - [[new_words_1(function)]] initializes the object with a single word.
-  !! - [[new_words_str(function)]] initializes the object with a string that is 
-  !!   splitted according to a given delimiter.
-  INTERFACE new_words
-    MODULE PROCEDURE new_words_1, new_words_str
-  END INTERFACE
-  
-  !> Clear either a scalar or a vector of list of [[words(type)]]
-  !!
-  !! The interface encapsulates words _destructors_, that deallocate memory used 
-  !! by the given list(s) of words. This method should be called anytime words 
-  !! object(s) is no longer used to avoid memory leaks.
-  !! @note
-  !! If the library support Derived type finalization, calling destructor is not
-  !! mandatory.
-  INTERFACE words_clear
-    MODULE PROCEDURE ws_clear_sc, ws_clear_ve
-  END INTERFACE
-
-  !> Extend a given [[words(type)]] object either by another or by a string
-  !!
-  !! The interface encapsulates two subroutines:
-  !!
-  !! - [[ws_extend_ws(subroutine)]](this,other) which extends __this__ by __other__ 
-  !!   (both are words objects).
-  !! - [[ws_extend_str(subroutine)]](this,str,delimiter,merge) which splits __str__
-  !!   according to __delimiter__ (and optionally __merge__) and then extends 
-  !!   __this__ with the resulting tokens.
-  INTERFACE words_extend
-    MODULE PROCEDURE ws_extend_ws,ws_extend_str
-  END INTERFACE
-
-  !> Convert an intrinsic type value to a string
-  !!
-  !! This (very) generic interface provides conversion functions from
-  !! intrinsic types to ALLOCATED string.
-  !!
-  !! ```
-  !! (1)  FUNCTION to_string(value)               RESULT(str)
-  !! (2)  FUNCTION to_string(value,fmt,width)     RESULT(str)
-  !! ```
-  !! Where :
-  !!
-  !! - __value__ is the value to convert
-  !! - __fmt__ is a string the format descriptor of the output string. Surrounding
-  !!   parenthesis can be omitted.
-  !! - __width__ is an integer with the width of the output string (which should be given
-  !!   in __fmt__ anyway).
-  !! - __str__ is an allocatable string with the converted value in output, or an empty
-  !!   string if the conversion failed. 
-  INTERFACE to_string
-    MODULE PROCEDURE int2str_as,int2str_fs
-    MODULE PROCEDURE log2str_as,log2str_fs
-    MODULE PROCEDURE real2str_as,real2str_fs
-    MODULE PROCEDURE dble2str_as,dble2str_fs
-    MODULE PROCEDURE cplx2str_as,cplx2str_fs
-  END INTERFACE
-  
-  !> Convert a string into an intrisinc type
-  !!
-  !! All methods defined in the interface are functions which take in arguments,
-  !! a string (input) and an output variable with the relevant type (or vectors of both). 
-  !! They always return an error object which is set to -5 error code (i.e. cannot cast value)
-  !! on error, otherwise [[errors(module):noerror(variable)]].
-  INTERFACE from_string
-    MODULE PROCEDURE str2int_sc,str2log_sc,str2real_sc,str2dble_sc,str2cplx_sc
-    MODULE PROCEDURE str2int_ve,str2log_ve,str2real_ve,str2dble_ve,str2cplx_ve
-  END INTERFACE
-  
-  !> Define a linked word
-  !!
-  !! Linked words are only intended to be used within a words type.
-  !! It's part of the doubly linked list words.
-  TYPE, PUBLIC :: word
-#if HAVE_FTNDTSTR    
-    CHARACTER(len=:), ALLOCATABLE :: value !! Value of the word
-#else    
-    !> Value of the word
-    !!
-    !! @warning 
-    !! It is always limited to [[strings(module):st_slen(variable)]] characters.
-    CHARACTER(len=st_slen)        :: value = ''
-#endif
-    TYPE(word), PRIVATE, POINTER  :: next => null() !! Next word in the list of words
-    TYPE(word), PRIVATE, POINTER  :: prev => null() !! Previous word in the list of words
-  END TYPE word
-  
-  !> Define a list of words
-  TYPE, PUBLIC :: words
-    INTEGER :: nw = 0                              !! Number of word in the list
-    TYPE(word), PRIVATE, POINTER :: head => null() !! First word in the list
-    TYPE(word), PRIVATE, POINTER :: tail => null() !! Last word in the list
-    TYPE(word), PRIVATE, POINTER :: iter => null() !! Current word (iterator)
-#if HAVE_FTNPROC 
-    CONTAINS
-    PROCEDURE, PRIVATE :: ws_extend_ws
-    PROCEDURE, PRIVATE :: ws_extend_str
-    PROCEDURE, PUBLIC :: length      => words_length
-      !! Get the number of words in the list
-    PROCEDURE, PUBLIC :: insert      => words_insert
-      !! Insert a word at given index
-    PROCEDURE, PUBLIC :: append      => words_append
-      !! Append a word at the end of the list 
-    PROCEDURE, PUBLIC :: prepend     => words_prepend
-      !! Prepend a word at the beginning of the list 
-    PROCEDURE, PUBLIC :: get         => words_get
-      !! Get the word at given index
-    PROCEDURE, PUBLIC :: set         => words_set
-      !! Set a word at given index
-    PROCEDURE, PUBLIC :: max_width   => words_get_max_width
-      !! Get the width of the biggest word in the list
-    PROCEDURE, PUBLIC :: total_width => words_get_total_width
-      !! Get the total width of the words stored in the list
-    PROCEDURE, PUBLIC :: reverse     => words_reverse
-      !! Reverse the list in place
-    PROCEDURE, PUBLIC :: reversed    => words_reversed
-      !! Get a reversed copy of the list 
-    PROCEDURE, PUBLIC :: dump        => words_dump
-      !! Dump words of the list (on per line)
-    PROCEDURE, PUBLIC :: tostring    => words_to_string
-      !! Convert the list in a single string
-    PROCEDURE, PUBLIC :: to_vector   => words_to_vector
-      !! Convert the list in a vector
-    PROCEDURE, PUBLIC :: pop         => words_pop
-      !! Pop a word from the list and returns it 
-    PROCEDURE, PUBLIC :: remove      => words_remove
-      !! Remove a word from the list
-    PROCEDURE, PUBLIC :: next        => words_next
-      !! Go to the next word in the list
-    PROCEDURE, PUBLIC :: previous    => words_previous
-      !! Go to the previous word in the list
-    PROCEDURE, PUBLIC :: reset       => words_reset
-      !! Reset the list's iterator
-    PROCEDURE, PUBLIC :: valid       => words_valid
-      !! Check if iterator position is valid
-    PROCEDURE, PUBLIC :: current     => words_current
-      !! Get the current word in the list
-    GENERIC, PUBLIC :: extend => ws_extend_ws,ws_extend_str
-      !! Extend a list with either a string or another list of words
-#endif
-  END TYPE words
-  
-  CONTAINS
-  
-  FUNCTION word_length(this) RESULT(lgth)
-    !! Get the trimmed length of the word object
-    TYPE(word), INTENT(in) :: this
-      !! A word object
-    INTEGER :: lgth
-      !! The length of the word's value (without trailing spaces)
-#if HAVE_FTNDTSTR    
-    IF (.NOT.ALLOCATED(this%value)) THEN
-      lgth = 0 ; RETURN
-    ENDIF
-#endif    
-    lgth = LEN_TRIM(this%value)
-    RETURN
-  END FUNCTION word_length
-  
-  SUBROUTINE disconnect_word(this)
-    !! Disconnect a word object
-    !!
-    !! The object is no more connected to its neighbours which are connected together.
-    !! @note 
-    !! After this method is called the object is no longer connected to its parent words 
-    !! object and should be deallocated in order to avoid memory leaks.
-    TYPE(word), INTENT(inout) :: this
-      !! A word object to disconnect
-    TYPE(word), POINTER :: pw,nw
-    nw => this%next ; pw => this%prev
-    IF (ASSOCIATED(nw)) nw%prev => pw
-    IF (ASSOCIATED(pw)) pw%next => nw
-    RETURN
-  END SUBROUTINE disconnect_word
-  
-  FUNCTION new_words_1(value) RESULT(list)
-    !! words constructor
-    !!
-    !! The method initializes a list of words with a single value.
-    CHARACTER(len=*), INTENT(in), OPTIONAL :: value
-      !! An optional string with the value to set in the list
-    TYPE(words) :: list
-      !! A words object
-    IF (PRESENT(value)) CALL ini_word(list,value)
-    RETURN
-  END FUNCTION new_words_1
-  
-  FUNCTION new_words_str(string,delimiter,merge,protect) RESULT(list)
-    !! words constructor (extended version)
-    !!
-    !! The method splits an input strings and initializes a words object with the resulting tokens.
-    CHARACTER(len=*), INTENT(in)  :: string
-      !! A string used to initialize the words object
-    CHARACTER(len=*), INTENT(in)  :: delimiter
-      !! A string with the words delimiters. 
-      !!
-      !! Note that each character is seen as a single delimiter.
-    LOGICAL, INTENT(in), OPTIONAL :: merge
-      !! An optional boolean control flag that instructs the method
-      !! wether to merge or not successive delimiters (default to .false.)
-    LOGICAL, INTENT(in), OPTIONAL :: protect
-      !! An optional boolean flag with .true. (default) to indicate that delimiter characters 
-      !! between quotes are protected
-    TYPE(words) :: list
-      !! A new words object
-    CHARACTER(len=:), ALLOCATABLE :: seps
-    LOGICAL                       :: zmerge, zprotect
-    zmerge = .false. ; zprotect = .true. ; seps = ' '
-    IF (PRESENT(merge)) zmerge = merge
-    IF (PRESENT(protect)) zprotect = protect
-    IF (LEN(delimiter) > 0) seps = delimiter
-    CALL ws_extend_str(list,string,seps,zmerge,zprotect)
-    RETURN
-  END FUNCTION new_words_str
-  
-  SUBROUTINE ws_affect(this,other)
-    !! words object assignment operator subroutine
-    TYPE(words), INTENT(out) :: this
-      !! A words object to be assigned
-    TYPE(words), INTENT(in)  :: other
-      !! A words object to assign
-    TYPE(word), POINTER :: cur
-    CALL ws_clear_sc(this)
-    IF (other%nw == 0) THEN
-      RETURN
-    ELSE
-      cur => other%head
-      DO WHILE(associated(cur))
-#if HAVE_FTNDTSTR      
-        IF (.NOT.ALLOCATED(cur%value)) THEN
-          CALL words_append(this,"")
-        ELSE
-          CALL words_append(this,cur%value)
-        ENDIF
-#else
-        CALL words_append(this,cur%value)
-#endif
-        IF (ASSOCIATED(cur,other%iter)) this%iter => this%tail
-        cur => cur%next
-      ENDDO
-    ENDIF
-    RETURN
-  END SUBROUTINE ws_affect
-  
-  SUBROUTINE ini_word(this,value)
-    !! Initialize the first word of a list of words
-    !!
-    !! This subroutine is not a constructor. It is only intended to set the first word 
-    !! object in a words object.
-    TYPE(words), INTENT(inout)   :: this
-      !! A words object reference
-    CHARACTER(len=*), INTENT(in) :: value
-      !! A string with the word used to initialize the list 
-    ALLOCATE(this%head)
-    this%head%next => null()
-    this%head%prev => null()
-    this%tail => this%head
-    ASSIGN_DTSTR(value,this%tail%value)
-    this%nw = 1
-    RETURN
-  END SUBROUTINE ini_word
-
-  SUBROUTINE ws_clear_sc(obj)
-    !! Clear a list of words
-    !!
-    !! This subroutine deallocates all memory used by the given words object.
-    !! @warning 
-    !! The subroutine should be called whenever a words is no more used (e.g. at 
-    !! the end of the current scope), otherwise memory leaks could occur.
-    TYPE(words),INTENT(inout), TARGET :: obj
-      !! A words object to clear
-    TYPE(word), POINTER :: cur,next
-    IF (obj%nw == 0) RETURN
-    cur => obj%head
-    DO WHILE(ASSOCIATED(cur))
-      next => cur%next
-      CALL disconnect_word(cur)
-#if HAVE_FTNDTSTR
-      IF (ALLOCATED(cur%value)) DEALLOCATE(cur%value)
-#endif
-      DEALLOCATE(cur)
-      cur => next
-    ENDDO
-    obj%nw = 0
-    obj%head => null() ; obj%tail => null()
-    obj%iter => null()
-  END SUBROUTINE ws_clear_sc
-
-  SUBROUTINE ws_clear_ve(objs)
-    !! Clear a vector of list of words
-    !!
-    !! This subroutine deallocates all memory used by the given vector of words objects.
-    !! @warning 
-    !! The subroutine should be called whenever a words is no more used (e.g. at the end 
-    !! of the current scope), otherwise memory leaks could occur.
-    TYPE(words),INTENT(inout), DIMENSION(:) :: objs
-      !! A vector of words objects to clear
-    TYPE(word), POINTER :: cur,next
-    INTEGER             :: i
-    DO i=1,SIZE(objs)
-      IF (objs(i)%nw == 0) CYCLE
-      cur => objs(i)%head
-      DO WHILE(ASSOCIATED(cur))
-        next => cur%next
-        CALL disconnect_word(cur)
-        DEALLOCATE(cur)
-        cur => next
-      ENDDO
-      objs(i)%nw = 0
-      objs(i)%head => null() ; objs(i)%tail => null()
-    ENDDO
-  END SUBROUTINE ws_clear_ve
-
-  SUBROUTINE ws_extend_ws(this, other)
-    !! Extend a list of words with another one
-    OBJECT(words), INTENT(inout) :: this
-      !! A words object to extend
-    TYPE(words), INTENT(in)     :: other
-      !! A words object to extend with
-    TYPE(word), POINTER :: cw
-    IF (other%nw == 0) RETURN
-    cw => other%head
-    DO WHILE(ASSOCIATED(cw))
-      CALL words_append(this,cw%value) ; cw => cw%next
-    ENDDO
-    RETURN
-  END SUBROUTINE ws_extend_ws
-
-  SUBROUTINE ws_extend_str(this,str,delimiter,merge,protect) 
-    !> Extend a list of word with a given string
-    !! @details The method adds a new list of words to the current list by 
-    !! splitting a string using a set of delimiters.
-    !! 
-    !!   - If __delimiter__ is not given, THEN blank space is used.
-    !!   - __delimiter__ can be a string of any length, but each character of 
-    !!     the sequence is seen as a single delimiter. Each time one of these 
-    !!     special character is seen on the string, it is splitted.
-    !!   - If __protect__ is set to .true. THEN delimiter enclosed by
-    !!     either single or double quotes are protected.
-    !!   - The optional argument __merge__ instructs the method wether to merge
-    !!     or not successive delimiters in the string.
-    !! 
-    !! For example, considering the following string:
-    !! <center>@verbatim "I like coffee and bananas." @endverbatim</center>
-    !!   - Used with only __delimiter__ = " e", the method returns the list:
-    !!     <center>"I","lik","","coff","","","and","bananas"</center>
-    !!   - Used with both __delimiter__ = " e" and __merge__ = .true. :
-    !!     <center>"I","lik","coff","and","bananas"</center>
-    !! @warning
-    !! The method does not trim or adjust the input string. Consequently, it can
-    !! add several empty words at the end of the list if the string is not well
-    !! defined.
-    !! @warning To avoid such problems, consider using TRIM() and ADJUSTL()
-    !! function on __str__ actual argument when calling this subroutine.
-    OBJECT(words), INTENT(inout), TARGET   :: this
-      !! A words object to extend
-    CHARACTER(len=*), INTENT(in)           :: str
-      !! A string to split in words
-    CHARACTER(len=*), INTENT(in), OPTIONAL :: delimiter
-      !! An optional string with the words delimiters (default to blank space). 
-    LOGICAL, INTENT(in), OPTIONAL          :: merge
-      !! An optional boolean control flag that instructs the method
-      !! wether to merge or not successive delimiters (default to .false.)
-    LOGICAL, INTENT(in), OPTIONAL          :: protect
-      !! An optional boolean flag with .true. to indicate that 
-      !! delimiter characters between quotes are protected
-    ! - LOCAL
-    INTEGER                       :: sl,p,i,j,stat
-    LOGICAL                       :: zmerge,zprotect,indq,insq,outer
-    CHARACTER(len=:), ALLOCATABLE :: seps
-    CHARACTER(len=:), ALLOCATABLE :: curw
-    CHARACTER(len=1), PARAMETER   :: sq = CHAR(39) ! single quote ascii code
-    CHARACTER(len=1), PARAMETER   :: dq = CHAR(34) ! double quotes ascii code
-    stat=0 ; p=1 ; indq = .false. ; insq = .false. 
-    seps = ' '
-    zmerge = .false. ; IF (PRESENT(merge)) zmerge = merge
-    zprotect = .true. ; IF (PRESENT(protect)) zprotect = protect
-    IF (PRESENT(delimiter)) THEN
-      IF (LEN(delimiter) > 0) seps = delimiter
-    ENDIF
-    sl = LEN(str) ; IF (sl == 0) RETURN
-    outer =     (INDEX(str,sq) == 1 .AND. INDEX(str,sq,.true.) == LEN(str)) & 
-            .OR.(INDEX(str,dq) == 1 .AND. INDEX(str,dq,.true.) == LEN(str))
-    ! no delimiter found or (have outer quotes and should protect)
-    IF (SCAN(str,seps) == 0.OR.(outer.AND.zprotect)) THEN
-      CALL words_append(this,remove_quotes(str)) 
-      RETURN
-    ENDIF
-    ! We have to loop... 
-    i = 1 ; curw=''
-    DO
-      IF (i > sl) EXIT
-      p = SCAN(str(i:),seps)
-      IF (p == 0) THEN
-        ! a gerer
-        curw = curw//TRIM(str(i:))
-        CALL words_append(this,TRIM(str(i:))) ; EXIT
-        curw=''
-      ELSE
-        IF (zprotect) THEN
-          j=i
-          ! starting state
-          DO WHILE(j<i+p)
-            IF (str(j:j) == sq.AND. .NOT.indq) insq = .NOT.insq
-            IF (str(j:j) == dq.AND. .NOT.insq) indq = .NOT.indq
-            j = j+1
-          ENDDO
-          IF ((insq.AND.INDEX(str(j:),"'")/=0) .OR. &
-              (indq.AND.INDEX(str(j:),'"')/=0)) THEN
-            curw=curw//str(i:i+p-1)
-            i=i+p ; CYCLE
-          ENDIF
-        ENDIF
-        IF (p == 1) THEN
-          IF (.NOT.zmerge) THEN
-            curw=''
-            CALL words_append(this,curw)
-          ENDIF
-          i = i + 1 ; CYCLE
-        ELSE
-          curw=curw//str(i:i+p-2)
-          CALL words_append(this,curw)
-          curw = ''
-          i = i + p
-        ENDIF
-      ENDIF
-    ENDDO
-    IF (zprotect) THEN
-      ! catching unbalanced quotes
-      IF (insq .OR. indq) &
-      WRITE(*,'(a)') "extends:warning: unbalanced quotes"
-    ENDIF
-    RETURN
-  END SUBROUTINE ws_extend_str
-
-  FUNCTION ws_get_ptr(this,idx) RESULT(pted)
-    !! Get the pointer of the word object at given index
-    !!
-    !! The method returns the pointer of the word object at the given index. 
-    !! If index is out of range a null poitner is returned.
-    OBJECT(words), INTENT(in) :: this
-      !! A words object 
-    INTEGER, INTENT(in)       :: idx
-      !! An integer with the index of the desired object in __this__
-    TYPE(word), POINTER :: pted
-      !! A pointer to the selected word object.
-    INTEGER :: i
-    pted => null()
-    IF (idx < 1 .OR. idx > words_length(this)) THEN
-      RETURN
-    ENDIF
-    IF (idx > (this%nw+1)/2) THEN
-      pted => this%tail
-      DO i=1,this%nw - idx ; pted => pted%prev ; ENDDO
-    ELSE
-      pted => this%head
-      DO i=1,idx-1 ; pted => pted%next ; ENDDO
-    ENDIF
-    RETURN
-  END FUNCTION ws_get_ptr
-
-  FUNCTION words_length(this) RESULT(res)
-    !! Get the size of the words object.
-    !!
-    !! The method returns the number of words stored in the given list of words.
-    OBJECT(words), INTENT(in) :: this !! A words object.
-    INTEGER :: res                    !! The number of words in the object.
-    res = this%nw
-    RETURN
-  END FUNCTION words_length
-
-  SUBROUTINE words_insert(this, idx, value)
-    !! Insert a word before given index in a list of words.
-    !!
-    !! The method inserts a new word before the given index in the list of words. If the given index is out
-    !! of range, the method prepends/appends the object based on the index value.
-    OBJECT(words), INTENT(inout)  :: this
-      !! A words object.
-    INTEGER, INTENT(in)          :: idx
-      !! An integer with the index of an object in the list. The new object will be inserted before that index.
-    CHARACTER(len=*), INTENT(in) :: value
-      !! A string with the word to insert in the list.
-    TYPE(word), POINTER :: welt,nx,pv
-    INTEGER             :: i
-    welt => null() ; nx => null() ; pv => null()
-    IF (this%nw == 0) THEN
-      CALL ini_word(this,value)
-    ELSE IF (idx > this%nw) THEN
-      this%nw = this%nw + 1
-      welt => this%tail
-      allocate(this%tail)
-      ASSIGN_DTSTR(value,this%tail%value)
-      this%tail%prev => welt
-      this%tail%prev%next => this%tail
-    ELSE IF (idx <= 1) THEN
-      this%nw = this%nw + 1
-      welt => this%head
-      allocate(this%head)
-      ASSIGN_DTSTR(value,this%head%value)
-      this%head%next => welt
-      this%head%next%prev => this%head
-    ELSE
-      IF (idx > (this%nw+1)/2) THEN
-        nx => this%tail 
-        DO i=1, this%nw - idx ; nx => nx%prev ; ENDDO
-      ELSE
-        nx => this%head 
-        DO i=1, idx-1 ; nx => nx%next ; ENDDO
-      ENDIF
-      pv => nx%prev
-      allocate(welt)
-      ASSIGN_DTSTR(value,welt%value)
-      welt%prev => pv ; welt%next => nx
-      pv%next => welt ; nx%prev => welt
-      this%nw = this%nw + 1
-    ENDIF
-    RETURN
-  END SUBROUTINE words_insert
-
-  SUBROUTINE words_append(this,value)
-    !! Append a word to the list of word
-    !! 
-    !! The method appends a word to the list of word. This is a convinient wrapper to 
-    !! [[strings(module)::words_insert(subroutine)] to add a new word at the beginning of the list.
-    OBJECT(words), INTENT(inout) :: this  !! A words object 
-    CHARACTER(len=*), INTENT(in) :: value !! A string to append
-    CALL words_insert(this,this%nw+1,value)
-    RETURN
-  END SUBROUTINE words_append
-
-  SUBROUTINE words_prepend(this,value)
-    !! Prepend a word to the list of word
-    !!
-    !! The method prepends a word to the list of word. This is a convinient wrapper to
-    !! [[strings(module)::words_insert(subroutine)]] to add a new word at the end of the list.
-    OBJECT(words), INTENT(inout) :: this  !! A words object 
-    CHARACTER(len=*), INTENT(in) :: value !! A string to prepend
-    CALL words_insert(this,0,value)
-    RETURN
-  END SUBROUTINE words_prepend
-
-  FUNCTION words_get(this,idx,case) RESULT (res)
-    !! Get the word's value at given index
-    !! 
-    !! The method attempts to get the word's value at the given index. If index is out of range
-    !! an empty string is returned.
-    !! @note 
-    !! The returned string is always trimmed.
-    OBJECT(words), INTENT(in)              :: this
-      !! A words object reference
-    INTEGER, INTENT(in)                    :: idx
-      !! An integer with the index of a word in the list
-    CHARACTER(len=5), INTENT(in), OPTIONAL :: case 
-      !! An optional string with either 'upper' or 'lower' to get the value converted in the relevant case
-    CHARACTER(len=:), ALLOCATABLE :: res
-      !! The value of the word stored at given index in the list of words
-    TYPE(word), POINTER :: cur
-    cur => ws_get_ptr(this,idx)
-    IF (.not.associated(cur)) THEN
-      res = '' ; RETURN
-    ENDIF
-    IF (PRESENT(case)) THEN
-      IF (case == "upper") res = str_to_upper(cur%value) 
-      IF (case == "lower") res = str_to_lower(cur%value)
-    ELSE
-      res = TRIM(cur%value)
-    ENDIF
-    RETURN
-  END FUNCTION words_get
-
-  SUBROUTINE words_set(this,idx,value)
-    !! Set a new value to a word object in the list of words at given index
-    !!
-    !! The method sets a new word at given index. If index is out of range, the method simply does nothing.
-    OBJECT(words), INTENT(inout) :: this  !! A words object
-    INTEGER, INTENT(in)          :: idx   !! An integer with the index of the word object to modify in the list
-    CHARACTER(len=*), INTENT(in) :: value !! A string with the new value to set
-    TYPE(word), POINTER :: cur
-    cur => ws_get_ptr(this,idx)
-    IF (.NOT.ASSOCIATED(cur)) RETURN
-    cur%value = value
-  END SUBROUTINE words_set
-
-  FUNCTION words_get_max_width(this) RESULT(res)
-    !! Get the longest word's width in the words object
-    !!
-    !! The method computes and returns the longest (trimmed) word's width in the words object.
-    OBJECT(words), INTENT(in) :: this !! A words object 
-    INTEGER :: res                    !! An integer with the maximum width (0 if the list is empty)
-    TYPE(word), POINTER :: cur
-    res = 0
-    IF (this%nw == 0) RETURN
-    cur => this%head ; res = word_length(cur)
-    DO WHILE(ASSOCIATED(cur%next))
-      cur => cur%next
-      IF (word_length(cur) > res) res = word_length(cur)
-    ENDDO
-    RETURN
-  END FUNCTION words_get_max_width
-
-  FUNCTION words_get_total_width(this) RESULT(width)
-    !! Get the total width of all words stored in the list of words
-    !! 
-    !! The method computes and returns the total width of all words stored in 
-    !! the list of words.
-    !! @note 
-    !! Total width is computed using strings::word_length so it only takes
-    !! into account trimmed words (without trailing blanks)
-    !! @note 
-    !! If csi codes have been added to words elements they are counted in the width.
-    OBJECT(words), INTENT(in) :: this !! A words object
-    INTEGER :: width                  !! Total length of the list of words
-    TYPE(word), POINTER :: cur
-    width = 0
-    IF (this%nw == 0) RETURN
-    cur => this%head ; width = word_length(cur)
-    DO WHILE(ASSOCIATED(cur%next))
-      cur => cur%next
-      width = width + word_length(cur)
-    ENDDO
-    cur => null()
-    RETURN
-  END FUNCTION words_get_total_width
-
-  SUBROUTINE words_reverse(this)
-    !! Reverse the list of words in-place
-    OBJECT(words), INTENT(inout) :: this
-      !! A words object to reverse
-    TYPE(word), POINTER :: loop,iwc,iwp
-    IF (this%nw <= 1) RETURN
-    loop => this%head ; iwc=> this%head ; iwp=> null()
-    DO WHILE(ASSOCIATED(loop%next))
-      loop => loop%next
-      iwp => iwc%prev ; iwc%prev => iwc%next ; iwc%next => iwp
-      iwc => loop
-    ENDDO
-    iwp=>this%tail%prev ; this%tail%prev=>this%tail%next ; this%tail%next=>iwp
-    iwc => this%head ; this%head => this%tail ; this%tail => iwc
-    loop => null() ; iwc => null() ; iwp => null()
-    RETURN
-  END SUBROUTINE words_reverse
-
-  FUNCTION words_reversed(this) RESULT(res)
-    !! Get a reversed copy of the list of words
-    OBJECT(words), INTENT(in) :: this
-      !! A words object to reverse
-    TYPE(words) :: res
-      !! A reversed copy of the input list of words
-    TYPE(word),POINTER  :: cur
-    IF(this%nw == 0) RETURN
-    cur => this%tail
-    DO WHILE(ASSOCIATED(cur))
-      CALL words_append(res,cur%value)
-      IF (ASSOCIATED(cur,this%iter)) res%iter => res%tail 
-      cur => cur%prev
-    ENDDO
-    cur => null()
-    RETURN
-  END FUNCTION words_reversed
-
-  SUBROUTINE words_dump(this,lun)
-    !! Dump the list of words
-    !! 
-    !! The method dumps on the given logical unit the elements of the list one by line.
-    OBJECT(words), INTENT(in)     :: this
-      !! A words object to dump
-    INTEGER, INTENT(in), OPTIONAL :: lun
-      !! An optional integer with the printing logical unit. If not given, the list is dumped on 
-      !! standard output stream.
-    TYPE(word), POINTER :: cur
-    INTEGER             :: lu
-    IF (this%nw == 0) RETURN
-    lu=6 ; IF (PRESENT(lun)) lu = lun
-    cur => this%head
-    DO WHILE(ASSOCIATED(cur))
-      WRITE(lu,'(a)') TRIM(cur%value)
-      cur => cur%next
-    ENDDO
-    cur => null()
-    RETURN
-  END SUBROUTINE words_dump
-
-  FUNCTION words_to_string(this, delimiter) RESULT(str)
-    !! Convert the list of words into a string
-    !!
-    !! The method converts the list of words into a string. In output, string is always
-    !! allocated even if the list is empty.
-    OBJECT(words), INTENT(in)              :: this
-      !! A words object 
-    CHARACTER(len=*), INTENT(in), OPTIONAL :: delimiter
-      !! An optional string used as delimiter between each words
-    CHARACTER(len=:), ALLOCATABLE :: str
-      !! An allocatable string with the list of words joined by the given delimiter (if any)
-    TYPE(word), POINTER :: cur
-    str = ''
-    IF (this%nw == 0) RETURN
-    cur => this%head
-    DO WHILE(ASSOCIATED(cur))
-      str=str//TRIM(cur%value)
-      IF (PRESENT(delimiter).AND..NOT.ASSOCIATED(cur,this%tail)) &
-        str=str//delimiter
-      cur => cur%next
-    ENDDO
-    RETURN
-  END FUNCTION words_to_string
-
-  FUNCTION words_to_vector(this,ret) RESULT(ok)
-    !! Convert the list of words into a vector of strings
-    !!
-    !! The method attempts to convert the list of words in a vector of strings.
-    !! If _this_ list of words is empty, the output vector is allocated with 0 elements and the method returns
-    !! .false., otherwise it returns .true.
-    !! @note
-    !! If elements in __this__ words object are wider than [[strings(module):st_slen(variable)]], output 
-    !! values will be truncated.
-    OBJECT(words), INTENT(in)                                      :: this
-      !! A words object reference
-    CHARACTER(len=st_slen), INTENT(out), ALLOCATABLE, DIMENSION(:) :: ret
-      !! An allocatable vector of assumed length string with the words of __this__
-    LOGICAL             :: ok
-      !! Return status.
-    INTEGER             :: l,mw
-    TYPE(word), POINTER :: iw
-    ok = .true.
-    l = words_length(this)
-    IF (l == 0) THEN
-      ALLOCATE(ret(0))
-      ok = .false.
-      RETURN
-    ENDIF
-    ALLOCATE(ret(l)) ; mw = LEN(ret(l))
-    ret(1:l) = ' ' ! really needed ?
-    iw => this%head ; l=1
-    DO WHILE(ASSOCIATED(iw))
-       ret(l) = TRIM(iw%value) ; l=l+1 ; iw => iw%next
-    ENDDO
-  END FUNCTION words_to_vector
-
-  FUNCTION words_pop(this,idx,move_forward) RESULT(value)
-    !! Pop a word in the list of words
-    !!
-    !! The method removes the word of the list at given index and returns it. If no index is given, 
-    !! last word of the list is removed.
-    !!
-    !! If the index is out of range, the method does nothing and returns an empty string.
-    !!
-    !! By default, if the iterator is located on the item to be removed, it is moved backward before
-    !! deletion occurs. If __move\_forward__ is set to .true., the iterator is moved forward.
-    OBJECT(words), INTENT(inout)  :: this
-     !! A words object
-    INTEGER, INTENT(in), OPTIONAL :: idx
-      !! Optional index of the word to delete
-    LOGICAL, INTENT(in), OPTIONAL :: move_forward 
-      !! Move the iterator forward if needed. By default the iterator is moved backward. 
-    CHARACTER(len=:), ALLOCATABLE :: value
-      !! The word's value at given index 
-    LOGICAL             :: zforward
-    INTEGER             :: zidx
-    TYPE(word), POINTER :: cur
-    zidx=words_length(this) ; IF (PRESENT(idx)) zidx = idx
-    zforward = .false. ; IF (PRESENT(move_forward)) zforward = move_forward
-    cur => ws_get_ptr(this,zidx)
-    IF (.NOT.ASSOCIATED(cur)) THEN
-      value = '' ; RETURN
-    ELSE IF (ASSOCIATED(cur,this%iter)) THEN
-      IF (zforward) THEN
-        CALL words_next(this)
-      ELSE
-        CALL words_previous(this)
-      ENDIF
-    ENDIF
-    value = TRIM(cur%value)
-    CALL disconnect_word(cur)
-    DEALLOCATE(cur)
-    this%nw = this%nw - 1
-    RETURN
-  END FUNCTION words_pop
-
-  SUBROUTINE words_remove(this,idx,move_forward)
-    !! Remove the word of the list at given index
-    !!
-    !! The method removes the word of the list at given index. If no index is given, last word 
-    !! of the list is removed.
-    !!
-    !! If the index is out of range, the method does nothing.
-    !!
-    !! By default, if the iterator is located on the item to be removed, it is moved backward before
-    !! deletion occurs. If __move\_forward__ is set to .true., the iterator is moved forward.
-    OBJECT(words), INTENT(inout)  :: this
-      !! A words object
-    INTEGER, INTENT(in), OPTIONAL :: idx
-      !! Index of the word to delete
-    LOGICAL, INTENT(in), OPTIONAL :: move_forward
-      !! Move the iterator forward if needed. By default the iterator is moved backward. 
-    LOGICAL             :: zforward 
-    INTEGER             :: zidx
-    TYPE(word), POINTER :: cur
-    zidx=words_length(this) ; IF(PRESENT(idx)) zidx = idx
-    zforward = .false. ; IF (PRESENT(move_forward)) zforward = move_forward
-    cur => ws_get_ptr(this,idx)
-    IF (.NOT.ASSOCIATED(cur)) THEN
-      RETURN
-    ELSE IF (ASSOCIATED(cur,this%iter)) THEN
-      IF (zforward) THEN
-        CALL words_next(this)
-      ELSE
-        CALL words_previous(this)
-      ENDIF
-    ENDIF
-    CALL disconnect_word(cur)
-    DEALLOCATE(cur)
-    this%nw = this%nw - 1
-    RETURN
-  END SUBROUTINE words_remove
-
-  SUBROUTINE words_next(this)
-    !! Go to the next word in the list
-    OBJECT(words), INTENT(inout) :: this !! A words object
-    IF (ASSOCIATED(this%iter)) this%iter => this%iter%next
-  END SUBROUTINE words_next
-
-  SUBROUTINE words_previous(this)
-    !! Go to the previous word in the list
-    OBJECT(words), INTENT(inout) :: this !! A words object
-    IF (ASSOCIATED(this%iter)) this%iter => this%iter%prev
-  END SUBROUTINE words_previous
-
-  FUNCTION words_valid(this) RESULT(ret)
-    !! Check if the current iterated word is valid 
-    OBJECT(words), INTENT(in) :: this !! A words object
-    LOGICAL :: ret                    !! A logical flag with .true. if the current iterated word is valid
-    ret = associated(this%iter)
-  END FUNCTION words_valid
-
-  FUNCTION words_current(this) RESULT(wrd)
-    !! Get current word value
-    OBJECT(words), INTENT(in) :: this
-      !! A words object
-    CHARACTER(len=:), ALLOCATABLE :: wrd
-      !! A string with the value of the current word or __an unallocated string__ if current word 
-      !! is not valid (see [[strings(module):words_valid(function)]]).
-    IF (ASSOCIATED(this%iter)) THEN
-      wrd = this%iter%value
-    ENDIF
-  END FUNCTION words_current
-
-  SUBROUTINE words_reset(this,to_end)
-    !! Reset the iterator 
-    !!
-    !! The method resets the iterator either at the beginning or at the end of the list of words 
-    !! (if __to_end__ is set to .true.).
-    OBJECT(words), INTENT(inout)  :: this   !! A words object 
-    LOGICAL, INTENT(in), OPTIONAL :: to_end !! An optional logical flag with .true. to reset the iterator at the end of the list
-    this%iter => this%head
-    IF (PRESENT(to_end)) THEN
-      IF (to_end) this%iter => this%tail
-    ENDIF
-  END SUBROUTINE words_reset
-
-  ! Fancy string methods
-  ! --------------------
-
-  FUNCTION tokenize(str,vector,delimiter,merge,protect) RESULT(ok)
-    !! Tokenize a string.
-    CHARACTER(len=*), INTENT(in)                             :: str
-      !! A string to tokenize
-    CHARACTER(len=*), INTENT(out), DIMENSION(:), ALLOCATABLE :: vector
-      !! An allocatable vector of strings with the tokens found. If string cannot be tokenized, 
-      !! the vector is __allocated to 0 elements__ and the method returns .false..
-    CHARACTER(len=*), INTENT(in), OPTIONAL                   :: delimiter
-      !! An optional string with the words delimiters. It is set to blank space by default. 
-      !! Note that each character is seen as a single delimiter.
-    LOGICAL, INTENT(in), OPTIONAL                            :: merge
-      !! An optional boolean control flag with .true. that instructs the method wether to 
-      !! merge or not successive delimiters. 
-    LOGICAL, INTENT(in), OPTIONAL                            :: protect
-      !! An optional boolean flag with .true. to indicate that delimiter characters between 
-      !! quotes are protected.
-    LOGICAL :: ok
-      !! Return status (.true. on success)
-    CHARACTER(len=:), ALLOCATABLE :: seps
-    TYPE(words)                   :: tmp
-    LOGICAL                       :: zmerge,zprotect
-    zmerge = .false. ; zprotect = .true. ; seps = ' ' 
-    IF (PRESENT(merge)) zmerge = merge
-    IF (PRESENT(protect)) zprotect = protect
-    IF (PRESENT(delimiter)) THEN
-      IF (LEN(delimiter) > 0 ) seps = delimiter
-    ENDIF
-    tmp = new_words_str(str,seps,zmerge,zprotect)
-    ok = words_to_vector(tmp,vector)
-    CALL ws_clear_sc(tmp)
-    RETURN
-  END FUNCTION tokenize
-
-  FUNCTION remove_quotes(str) RESULT(ostr)
-    !! Strips outer quotes from string
-    !!
-    !! The function removes only external quotes from the input string
-    !! and returns the result in an allocatable string.
-    !! Quotes are removed only if they are the first and last non blank
-    !! characters. Either double and single quotes are stripped without distinction.
-    !! The output string is trimmed from leading and trailing blank spaces (after quotes removal !)
-    CHARACTER(len=*), INTENT(in)  :: str  !! A string to check
-    CHARACTER(len=:), ALLOCATABLE :: ostr !! A string without external quotes (if any).  
-    CHARACTER(len=1), PARAMETER   :: sq=CHAR(39), dq=CHAR(34)
-    CHARACTER(len=2), PARAMETER   :: dsq=CHAR(39)//CHAR(34)
-    INTEGER                       :: i, j
-    IF (LEN_TRIM(str) == 0) RETURN
-    ostr = TRIM(ADJUSTL(str))
-    i = SCAN(ostr,sq//dq) ; j = SCAN(ostr,sq//dq,.true.)
-    IF (i == j) RETURN
-    IF (i /= 1) i = 0
-    IF (j /= LEN(ostr)) j = LEN(ostr)+1
-    ostr = TRIM(ostr(i+1:j-1))
-    RETURN
-  END FUNCTION remove_quotes
-
-  FUNCTION string_is(str) RESULT(ret)
-    !! Check if string represents an intrinsic type
-    !!
-    !! The method checks if the given string represents an intrinsic type. Both logical and complex type 
-    !! are checked in a strict way :
-    !!
-    !! - A string is a logical if it is one of the following value: __.false.__, __.true.__, __F__, __T__.
-    !! - A string is potentially a complex if it has the following format: __(\*\*\*,\*\*\*)__ where 
-    !!   __\*\*\*__ is checked to see wether it is numerical or not.
-    !!
-    !! Valid numerical values can take the following forms:
-    !! ```
-    !!   [0-9]
-    !!   [0-9]*.?[0-9]*?([ed][+-]?[0-9]+)?
-    !! ```
-    !! Obviously if returned value is greater than 3, the string can be converted in 
-    !! floating point value.
-    !!
-    !! Empty input string is simply considered to be of string type !  
-    CHARACTER(len=*), INTENT(in) :: str
-      !! A string to check
-    INTEGER :: ret
-      !! An integer with the intrinsic type related to the string.
-      !!
-      !! Types are one of the following parameters
-      !!
-      !! - [[strings(module):st_string(variable)]] (1) for string
-      !! - [[strings(module):st_logical(variable)]] (2) for logical
-      !! - [[strings(module):st_complex(variable)]] (3) for complex
-      !! - [[strings(module):st_integer(variable)]] (4) for integer
-      !! - [[strings(module):st_real(variable)]] (5) for floating point value
-    CHARACTER(len=:), ALLOCATABLE :: zs,zzs
-    INTEGER :: j,l
-    ret = 1 ; IF (LEN_TRIM(str) == 0) RETURN
-    zs = str_to_lower(TRIM(ADJUSTL(str))) ; j = INDEX(zs,',') ; l = len(zs)
-    IF (zs(1:1)=='('.AND.zs(l:l) == ')'.AND.j==INDEX(zs,',')) THEN
-      IF (j == 2 .OR. j == l-1) RETURN
-      zzs = TRIM(ADJUSTL(zs(2:j-1))) ; IF (what_(zzs) < 3) RETURN
-      zzs = TRIM(ADJUSTL(zs(j+1:l-1))) ; ret = what_(zzs)
-      IF (ret > 3) THEN ; ret = 3 ; ELSE ; ret = 1 ; ENDIF
-    ELSE
-      ret = what_(zs)
-    ENDIF
-    CONTAINS
-      FUNCTION what_(s) RESULT(is)
-        !! Check if the given string is numerical, logical or a simple string
-        !! @note
-        !! Input string should be in lower case, otherwise, the method will give a a wrong result.
-        !! @warning
-        !! The test performed for logical checking is quite strict : A string is considered as logical
-        !! if and only if it is one of the following values : __.false.__, __.true.__, __F__, __T__.
-        CHARACTER(len=*), INTENT(in) :: s
-          !! A string to check
-        INTEGER :: is
-          !! An integer with : __1__ for string, __2__ for logical, __4__ for integer and __5__ for real
-        LOGICAL                      :: dec,fdot,fexp
-        INTEGER                      :: i
-        CHARACTER(len=24), PARAMETER :: aset='abcfghijklmnopqrstuvwxyz'
-        CHARACTER(len=10), PARAMETER :: iset='1234567890'
-        CHARACTER(len=2),  PARAMETER :: dset='ed'
-        CHARACTER(len=2),  PARAMETER :: sset='+-'
-        CHARACTER(len=7),  PARAMETER :: slog(4) = (/'.true. ','.false.',&
-                                                    't      ','f      '/)
-        is = -1 ; dec = .false. ; fdot = dec ; fexp = fdot
-        DO i = 1,LEN(s)
-          IF (i == 1) THEN
-            ! string does not start by [+-\.\d]
-            IF (VERIFY(s(i:i),'.'//iset//sset) /= 0) THEN
-              is = 1 ; EXIT
-            ENDIF
-            ! update control flag for decimal part
-            dec = s(i:i) == '.' ; fdot = dec
-          ELSE
-            ! check if char is in [a-z]
-            IF(VERIFY(s(i:i),aset) == 0) THEN
-              dec=.false. ; is = 1 ; EXIT
-            ELSE IF (s(i:i) == '.') THEN
-              ! check for dot in decimal/exponent part (==> not a number
-              IF (fdot.OR.fexp) THEN
-                dec = .false. ; is = 1 ; EXIT
-              ENDIF
-            ELSE IF (VERIFY(s(i:i),dset)==0) THEN
-              IF (fexp) THEN
-                dec = .false. ; is = 1 ; EXIT
-              ENDIF
-            ELSE IF (VERIFY(s(i:i),sset) == 0) THEN
-              IF (VERIFY(s(i-1:i-1),dset) /= 0) THEN
-                dec = .false. ; is = 1 ; EXIT
-              ENDIF
-            ENDIF
-            fdot = (fdot .OR. s(i:i) == '.')
-            fexp = (fexp .OR. VERIFY(s(i:i), dset) == 0)
-          ENDIF
-        ENDDO
-        ! it is a string
-        IF (is == 1) THEN
-          ! but have the format of a logical
-          IF (any(slog == s)) is = 2
-        ELSE
-          IF ((fexp.AND.SCAN(s(LEN(s):LEN(s)),dset) /= 0)) THEN
-            is = 1
-          ELSE
-            is = 4
-            IF (fdot.OR.fexp) is = 5
-          ENDIF
-        ENDIF
-      END FUNCTION what_
-  END FUNCTION string_is
-
-  FUNCTION format_string(str,idt1,idto) RESULT(output)
-    !! Format the given string
-    !!
-    !! This function only replaces all '\\n' escape sequence in the given string by NEW_LINE() character.
-    !! The output string is eventually indented if optional arguments are set.
-    !! @warning
-    !! __idto__ is relative to __idt1__ !
-    CHARACTER(len=*), INTENT(in)  :: str     !! The string to format
-    INTEGER, INTENT(in), OPTIONAL :: idt1, & !! An optional integer with the indentation level of the first output line (default to 0)
-                                     idto    !! An optional integer with the indentation level of all other output lines (default to 0)
-    CHARACTER(len=:), ALLOCATABLE :: output  !! An allocatable string with the output formatted string.
-    ! - LOCAL
-    INTEGER :: i,c,ti,mx
-    CHARACTER(len=:), ALLOCATABLE :: idts
-    IF (LEN_TRIM(str) == 0) THEN
-      ALLOCATE(output,source='') ; RETURN
-    ENDIF
-    i=0 ; IF (PRESENT(idt1)) i = MAX(i,idt1) 
-    ALLOCATE(CHARACTER(len=i) :: output) 
-    IF (i > 0) output(1:i) = CHAR(32) 
-    ! i0 is relative to i1 and must be >= 0
-    IF (PRESENT(idto)) i = MAX(i+idto,0)
-    ALLOCATE(CHARACTER(len=i+1) :: idts)
-    idts(1:1) = NEW_LINE('A') ; IF (i>1) idts(2:) = CHAR(32) 
-    ! Builds output string 
-    c=1 ; mx = LEN_TRIM(str)
-    i = INDEX(str(c:),'\n') ; ti = c+i-1
-    IF (i == 0) THEN
-      output=output//TRIM(str(ti+1:mx)) 
-    ELSE
-      output=output//TRIM(str(c:ti-1)) ; c=ti+2 
-      DO
-        i = INDEX(str(c:),"\n") ; ti = c+i-1
-        IF (i == 0) THEN
-          output=output//TRIM(str(ti+1:mx)) ; c = mx+1 
-        ELSE
-          output=output//idts//str(c:ti-1) ; c = ti+2
-        ENDIF
-        IF (c > mx) EXIT
-      ENDDO
-    ENDIF
-    ! print a newline if we have \n at the end of the string
-    IF (INDEX(TRIM(str),'\n',.true.) == mx-1.AND.TRIM(str) /= '\n') &
-    output=output//idts(1:1)
-  END FUNCTION format_string
-
-  FUNCTION format_paragraph(str,width,idt1,idto) RESULT(output)
-    !! Split and format a string over several lines 
-    !! 
-    !! The function splits an input string in words so output lines fit (almost) in __width__ characters. 
-    !! The method handles indentation level (defined as leading blank spaces). It also accounts for known 
-    !! csi (see [[strings(module):attributes(variable)]].
-    !! @note
-    !! Words are considered indivisible and thus output lines can sometimes exceed the maximum width if 
-    !! there is not enough space to put a word (with the associated indentation if given). The default
-    !! behavior in that case is to print the word in a new line (with the correct leading blank spaces).
-    !! @warning
-    !! If __width__, __idt1__ and/or __idto__ have inconsistent values (e.g. __width__ <= __idt1__), the
-    !! method still computes the paragraph, but each words will be set on a new line with the appropriate
-    !! indentation.
-    CHARACTER(len=*), INTENT(in)  :: str    !! string with the content to split 
-    INTEGER, INTENT(in)           :: width  !! An positive integer with the maximum width of a line
-    INTEGER, INTENT(in), OPTIONAL :: idt1   !! An optional integer with the indentation level of the first output line
-    INTEGER, INTENT(in), OPTIONAL :: idto   !! An optional integer with the indentation level of the other output lines
-    CHARACTER(len=:), ALLOCATABLE :: output !! An allocatable string with the output content
-    CHARACTER(len=:), ALLOCATABLE :: idts,zs
-    INTEGER                       :: l1,lo,zmx,zw,cc,j,jj,l
-    zw = abs(width) ; zs = strip_newline(str)
-    zmx = LEN_TRIM(zs)
-    IF (zmx == 0) THEN
-      ALLOCATE(output,source='') ; RETURN
-    ENDIF
-    l1=0 ; IF (PRESENT(idt1)) l1 = MAX(l1,idt1)
-    ALLOCATE(CHARACTER(len=l1) :: output)
-    IF (l1 > 0) output(1:l1) = CHAR(32)
-    lo=l1 ; IF (PRESENT(idto)) lo = MAX(l1+idto,0)
-    ALLOCATE(CHARACTER(len=lo+1) :: idts)
-    idts(1:1) = NEW_LINE('A') ; IF (lo>=1) idts(2:len(idts)) = CHAR(32)
-    ! Prints a message if user is just stupid...
-    IF (lo+1 > zw .OR. l1+1 > zw) THEN
-      output = str ; RETURN
-    ENDIF
-    ! check if can just return the string as is 
-    IF (zmx + l1 <= zw) THEN
-      output=output//TRIM(zs) ; RETURN
-    ENDIF
-    j=1 ; jj=1+l1 
-    DO 
-      ! Gets next blank in input string
-      cc = INDEX(TRIM(zs(j:)),CHAR(32))
-      ! no more blank
-      ! Gets total length of csi between zs(j:j+cc-1)
-      ! this value will be substracted to each length test
-      IF (cc == 0) THEN
-        l = csis_length(zs(j:)) 
-        IF (jj-1+LEN_TRIM(zs(j:))-l > zw) THEN
-          output = output//idts
-        ENDIF
-        output=output//TRIM(zs(j:))
-        EXIT ! we are at the last word : we must exit the infinite loop !
-      ELSE
-        l = csis_length(zs(j:j+cc-1)) 
-        IF (cc+jj-1-l > zw) THEN
-          output=output//idts//zs(j:j+cc-1) ; jj = lo+1+cc+1 - l
-        ELSE
-          output=output//zs(j:j+cc-1) ; jj = jj + cc - l
-        ENDIF
-      ENDIF
-      j = j + cc
-    ENDDO
-    CONTAINS
-    FUNCTION csis_length(str) RESULT(value)
-      ! - DUMMY
-      CHARACTER(len=*), INTENT(in) :: str
-      ! - RESULT
-      INTEGER :: value
-      ! - LOCAL
-      INTEGER :: jc,iesc,im
-      LOGICAL :: tcsi
-      value = 0 
-      jc=1
-      DO 
-        IF (jc>LEN(str)) EXIT
-        ! search for escape
-        iesc = INDEX(str(jc:),CHAR(27))
-        IF (iesc == 0) EXIT 
-        ! search for m
-        im = INDEX(str(jc+iesc:),"m")
-        ! no m in the string after ESC --> this could not be a csi
-        IF (im == 0) EXIT
-        ! check if this is really a csi and updates length
-        tcsi = is_csi(str(jc+iesc-1:jc+iesc+im-1))
-        jc = jc + iesc 
-        IF (tcsi) THEN
-          value=value+im+1
-          jc=jc+im
-        ENDIF
-      ENDDO
-    END FUNCTION csis_length
-  END FUNCTION format_paragraph
-
-  FUNCTION strip_newline(str,rpl) RESULT(stripped)
-    !! Replace newline escape sequences by spaces
-    !!
-    !! The function replaces newline (both '\\n' escape sequence and Fortran NEW_LINE() character) in the 
-    !! given string and returns the resulting string.
-    CHARACTER(len=*), INTENT(in)           :: str !! A string to process
-    CHARACTER(len=1), INTENT(in), OPTIONAL :: rpl !! A optional single character used as substitution of escape sequences (blank space by default)
-    CHARACTER(len=:), ALLOCATABLE :: stripped     !! An allocatable string with all newline sequences replaced by blank space or __rpl__ if given 
-    CHARACTER(len=1) :: zrp
-    INTEGER          :: i, j, ns 
-    zrp = CHAR(32) ; IF(PRESENT(rpl)) zrp = rpl
-    IF (str == NEW_LINE('A')) THEN
-      stripped = zrp ; RETURN 
-    ENDIF
-    ns = LEN_TRIM(str)
-    IF (ns == 0) THEN
-      ALLOCATE(stripped,source='') ; RETURN
-    ENDIF
-    ALLOCATE(CHARACTER(len=ns) :: stripped) ; stripped(1:ns) = CHAR(32)
-    i=1 ; j=1
-    DO 
-      IF (str(i:i) == NEW_LINE('A')) THEN
-        stripped(j:j) = zrp 
-      ELSE IF (i < ns) THEN
-          IF (str(i:i+1) == "\n") THEN
-            stripped(j:j) = zrp ; i=i+1
-          ELSE
-            stripped(j:j) = str(i:i) 
-          ENDIF
-      ELSE
-        stripped(j:j) = str(i:i) 
-      ENDIF
-      j=j+1 ; i=i+1
-      IF (i > ns .OR. j > ns) EXIT
-    ENDDO
-    IF (j < ns) stripped = stripped(1:j)
-    RETURN
-  END FUNCTION strip_newline
-
-  FUNCTION str_length(str) RESULT(res)
-    !! Get the length of the string object
-    !! 
-    !! The method computes the length of the string. It differs from LEN intrinsic function as
-    !! it does not account for extra-characters of csi codes.
-    CHARACTER(len=*), INTENT(in) :: str !! String to process
-    INTEGER :: res                      !! The actual length of string (i.e. does not account for csi codes)
-    CHARACTER(len=:), ALLOCATABLE :: tmp
-    res = 0 
-    IF (LEN(str) /= 0) THEN
-      tmp = str_reset_attributes(str)
-      res = LEN(tmp)
-      DEALLOCATE(tmp)
-    ENDIF
-    RETURN
-  END FUNCTION str_length
-
-  FUNCTION str_to_lower(str1) RESULT(str)
-    !! Convert the string in lower case
-    !!
-    !! The method converts the input string in lower case and accounts for
-    !! possible csi codes in the string.
-    CHARACTER(len=*), INTENT(in) :: str1 !! Input string to convert
-    CHARACTER(len=:), ALLOCATABLE :: str !! A copy of the string in lower case
-    INTEGER :: i,ic
-    IF (LEN(str1) /= 0) THEN
-      str = str1
-      DO i = 1, len(str1)
-        ic = ichar(str1(i:i))
-        IF (ic >= 65 .AND. ic < 90) str(i:i) = char(ic + 32)
-      ENDDO
-    ELSE
-      str=''
-    ENDIF
-  END FUNCTION str_to_lower
-
-  FUNCTION str_to_upper(str1) RESULT(str)
-    !! Convert the string in upper case
-    !!
-    !! The method converts the input string in upper case and accounts for
-    !! possible csi codes in the string.
-    CHARACTER(len=*), INTENT(in) :: str1 !! Input string to convert
-    CHARACTER(len=:), ALLOCATABLE :: str !! A copy of the string in upper case
-    INTEGER :: j,i,ic,icsi,lcsi
-    IF (LEN(str1) > 0) THEN
-      str = str1 
-      i = 1
-      DO
-        IF (i > LEN(str)) EXIT
-        icsi = str_index_of_csi(str(i:),lcsi)
-        IF (icsi == 0) THEN
-          ! no more csi the end of string is upper case converted
-          DO j=i,LEN(str)
-            ic = ichar(str(j:j))
-            IF (ic >= 97 .AND. ic < 122) str(j:j) = char(ic-32)
-          ENDDO
-          RETURN
-        ELSE IF (icsi == 1) THEN
-          i = i + lcsi
-        ELSE IF (icsi > 1) THEN
-          ! csi is not the first word: we convert in upper case until its
-          ! position THEN copy the csi and get back in the loop
-          DO j=i,i+icsi-2
-            ic = ichar(str(j:j))
-            IF (ic >= 97 .AND. ic < 122) str(j:j) = char(ic-32)
-          ENDDO
-          i = i + icsi + lcsi-1 
-        ENDIF
-      ENDDO
-    ELSE
-      str=''
-    ENDIF
-  END FUNCTION str_to_upper
-
- FUNCTION str_remove(string,substring,back,all) RESULT(str)
-   !! Remove substring from current string 
-   !! 
-   !! The function removes the first occurence of __substring__ in __string__ or all 
-   !! its occurences if __all__ is explicitly set to .true..
-    CHARACTER(len=*), INTENT(in)  :: string    !! A string to search in
-    CHARACTER(len=*), INTENT(in)  :: substring !! A string to search and removes from __string__
-    LOGICAL, INTENT(in), OPTIONAL :: back, &   !! An optional boolean flag with .true. to begin search at the end of the string
-                                     all       !! An optional boolean flag with .true. to remove all occurences of __substring__
-    CHARACTER(len=:), ALLOCATABLE :: str       !! An allocatable string with __substring__ occurence(s) removed
-    LOGICAL :: zb,za
-    INTEGER :: is,j,zboff
-    str=''
-    zb = .false. ; za = .false.
-    IF (PRESENT(back)) zb = back
-    IF (PRESENT(all)) za = all
-    IF (za) zb=.false.
-    zboff = 0 ; IF (zb) zboff = 1
-    IF (LEN(string) == 0) RETURN
-    j=1 
-    DO 
-      IF (j>LEN(string)) EXIT
-      ! search for substring
-      is = INDEX(string(j:),substring,back=zb)
-      IF (is == 0) THEN
-        ! substring is not found : we get the last part of the string and return
-        str = str//string(j:) ; RETURN 
-      ELSE IF (is == 1) THEN
-        j = j + LEN(substring)
-      ELSE
-        ! substring is not at the begin of the string : saves the string
-        str = str//string(j:j+is-2)
-        j = j + is+LEN(substring)-1
-      ENDIF
-      ! if we only want to str_remove ONE occurence we exit if substring
-      ! has been found
-      IF (.NOT.(is==0.OR.za)) EXIT 
-    ENDDO
-    IF (j <= LEN(string).AND..NOT.zb) str=str//string(j:)
-    RETURN 
-  END FUNCTION str_remove
-
- FUNCTION str_replace(string,old,new,back,all) RESULT(str)
-    !! Replace substring from current string 
-    !!
-    !! The function replaces the first occurence of __old__ in __string__ by 
-    !! __new__ or all its occurence(s) if __all__ is explicitly set to .true..
-    CHARACTER(len=*), INTENT(in)  :: string  !! A string to search in
-    CHARACTER(len=*), INTENT(in)  :: old,  & !! A string to search and replace
-                                     new     !! A string to substitute to __old__
-    LOGICAL, INTENT(in), OPTIONAL :: back, & !! An optional boolean flag with .true. to begin search at the end of the string
-                                     all     !! An optional boolean flag with .true. to replace all occurences of __old__
-    CHARACTER(len=:), ALLOCATABLE :: str     !! An allocatable string with occurence(s) of __old__ replaced by __new__
-    LOGICAL :: zb,za
-    INTEGER :: is,j
-    str=''
-    zb = .false. ; za = .false.
-    IF (PRESENT(back)) zb = back
-    IF (PRESENT(all)) za = all
-    IF (za) zb = .NOT.za
-    IF (LEN(string) == 0) RETURN 
-    j=1 
-    DO 
-      IF (j>LEN(string)) EXIT
-      ! search for "old"
-      is = INDEX(string(j:),old,back=zb)
-      IF (is == 0) THEN
-        ! "old" is not found : we get the last part of the string and return
-        str = str//string(j:) ; RETURN 
-      ELSE IF (is == 1) THEN
-        str = str//new
-        j = j + LEN(old)
-      ELSE
-        ! "old" is not at the begin of the string : saves the string
-        str = str//string(j:j+is-2)//new
-        j = j + is + LEN(old) - 1 
-      ENDIF
-      IF (.NOT.(is==0.OR.za)) EXIT 
-    ENDDO
-    IF (j <= LEN(str)) str=str//string(j:)
-    RETURN 
-  END FUNCTION str_replace
-
-  FUNCTION str_endswith(string,substring,icase) RESULT(ret)
-    !! Check if string ends by substring 
-    CHARACTER(len=*), INTENT(in)  :: string
-      !! @param[in] string A string to check
-    CHARACTER(len=*), INTENT(in)  :: substring
-      !! A string to search in __string__
-    LOGICAL, INTENT(in), OPTIONAL :: icase 
-      !! An optional boolean flag with .true. to perform insensitive case search
-    LOGICAL :: ret
-      !! .true. if __string__ ends by __substring__, .false. otherwise.
-    CHARACTER(len=:), ALLOCATABLE :: zthis,zstr
-    INTEGER                       :: idx 
-    LOGICAL                       :: noc 
-    ret = .false.
-    noc = .false. ; IF (PRESENT(icase)) noc = icase
-    IF (LEN(string) == 0 .OR. LEN(substring) == 0) RETURN
-    zthis = str_reset_attributes(string) ; zstr=str_reset_attributes(substring)
-    IF (noc) THEN
-      idx = INDEX(str_to_lower(zthis),str_to_lower(zstr),.true.)
-    ELSE
-      idx = INDEX(zthis,zstr,.true.)
-    ENDIF
-    IF (idx == 0.OR.idx+str_length(zstr)-1 /= str_length(zthis)) RETURN
-    ret=.true.
-  END FUNCTION str_endswith
-
-  FUNCTION str_startswith(string,substring,icase) RESULT(ret)
-    !! Check if string starts by substring 
-    CHARACTER(len=*), INTENT(in)  :: string
-      !! A string to check
-    CHARACTER(len=*), INTENT(in)  :: substring
-      !! A string to search in __string__
-    LOGICAL, INTENT(in), OPTIONAL :: icase 
-      !! An optional boolean flag with .true. to perform insensitive case search
-    LOGICAL :: ret
-      !! .true. if __string__ starts by __substring__, .false. otherwise.
-    CHARACTER(len=:), ALLOCATABLE :: zthis,zstr
-    INTEGER                       :: idx 
-    LOGICAL                       :: noc 
-    ret = .false.
-    noc = .false. ; IF (PRESENT(icase)) noc = icase
-    IF (LEN(string) == 0 .OR. LEN(substring) == 0) RETURN
-    zthis = str_reset_attributes(string) ; zstr=str_reset_attributes(substring)
-    IF (noc) THEN
-      idx = INDEX(str_to_lower(zthis),str_to_lower(zstr))
-    ELSE
-      idx = INDEX(zthis,zstr)
-    ENDIF
-    IF (idx /= 1) RETURN
-    ret=.true.
-  END FUNCTION str_startswith
-
-  ! CSI related functions 
-  ! ---------------------
-
-  FUNCTION str_add_attributes(string,attrs) RESULT(str)
-    !! Set csi attributes to the given string object
-    !!
-    !! The function adds csi (ANSI escape sequences) to the given string and
-    !! returns a copy of it.
-    CHARACTER(len=*), INTENT(in)      :: string
-      !! @param[in] string A string object reference 
-    INTEGER, INTENT(in), DIMENSION(:) :: attrs
-      !! A vector of integers with the code to add. Each __attrs__ value should refers to one i
-      !! of [[strings(module):attributes(variable)]] values.
-    CHARACTER(len=:), ALLOCATABLE :: str
-      !! An allocatable string with new csi codes added.
-    INTEGER                       :: j,iesc,im
-    CHARACTER(len=:), ALLOCATABLE :: tmp,csi
-    CHARACTER(len=4), PARAMETER   :: rcsi = CHAR(27)//"[0m"
-    str=''
-    ! 1) Check for input string
-    IF (LEN(string) == 0) RETURN
-    ! 2) Removes last <ESC>[0m if any and initializes output string
-    ! we must remove only the last <ESC>[0m if any
-    IF (INDEX(string,rcsi,.true.) == LEN(string)-3) THEN
-      tmp = str_remove(string,rcsi,back=.true.)
-    ELSE
-      tmp = string
-    ENDIF
-    ! 3) Add all the given csi preceded by <ESC>[0m at the beginning of the string 
-    !    if it does not start by an ANSI sequence
-    IF (INDEX(tmp,CHAR(27)//"[") /= 1) &
-    tmp = str_add_to_csi(rcsi,attrs)//tmp
-    ! Loops on new string and updates csi codes
-    j=1 
-    DO 
-      IF (j>LEN(tmp)) EXIT
-      ! search for escape
-      iesc = INDEX(tmp(j:),CHAR(27))
-      IF (iesc == 0) THEN
-        ! no more ESC : cat until end of input string and exit
-        str = str//tmp(j:) ; EXIT
-      ELSE IF (iesc > 1) THEN
-        ! ESC is not first char: copy until ESC
-        str = str//tmp(j:j+iesc-2)
-      ENDIF
-      ! search for m
-      im = INDEX(tmp(j+iesc:),"m")
-      ! no m in the string after ESC --> copy string (INCLUDING ESC) and leave
-      IF (im == 0) THEN
-        str = str//tmp(j+iesc-1:)
-        RETURN
-      ENDIF
-      csi = tmp(j+iesc-1:j+iesc+im-1)
-      ! we have a csi: we add new codes to it
-      IF (is_csi(csi)) THEN
-        csi = str_add_to_csi(csi,attrs)
-      ENDIF
-      str = str//csi
-      j = j + iesc + im
-    ENDDO
-    IF (INDEX(str,rcsi,.true.) /= LEN(str)-3) str = str//rcsi
-    RETURN 
-  END FUNCTION str_add_attributes
-
-  FUNCTION str_delete_attributes(string,attrs) RESULT(str)
-    !! Remove attributes to the given string
-    !!
-    !! The function removes list of csi (ANSI escape sequences) from the given 
-    !! string and returns a copy of it.
-    CHARACTER(len=*), INTENT(in)      :: string
-      !! Input string 
-    INTEGER, INTENT(in), DIMENSION(:) :: attrs 
-      !! A vector of integers with the code to remove. Each __attrs__ value should 
-      !! refers to one of [[strings(module):attributes(variable)]] values.
-    CHARACTER(len=:), ALLOCATABLE :: str
-      !! An allocatable string with csi codes from __list__ removed
-    LOGICAL                                           :: ok
-    INTEGER                                           :: j,iesc,im
-    CHARACTER(len=:), ALLOCATABLE                     :: tmp,csi,csis
-    CHARACTER(len=st_slen), DIMENSION(:), ALLOCATABLE :: tks
-    CHARACTER(len=4), PARAMETER                       :: rcsi = CHAR(27)//"[0m"
-    str=''
-    IF (LEN(string) == 0) RETURN
-    ! remove last <ESC>[0m if found at the end of the string
-    IF (INDEX(string,rcsi,.true.) == LEN(string)-3) THEN
-      tmp = str_remove(string,rcsi,back=.true.)
-    ELSE
-      tmp = string
-    ENDIF
-    ! Loops on new string and updates csi codes
-    j=1 ; csis=""
-    DO 
-      IF (j>LEN(tmp)) EXIT
-      ! search for escape
-      iesc = INDEX(tmp(j:),CHAR(27))
-      IF (iesc == 0) THEN
-        ! no more ESC : cat until end of input string and exit
-        str = str//tmp(j:) ; EXIT
-      ELSE IF (iesc > 1) THEN
-        ! ESC is not first char: copy until ESC
-        str = str//tmp(j:j+iesc-2)
-      ENDIF
-      ! search for m
-      im = INDEX(tmp(j+iesc:),"m")
-      ! no m in the string after ESC --> copy string (INCLUDING ESC) and leave
-      IF (im == 0) THEN
-        str = str//tmp(j+iesc-1:)
-        RETURN
-      ENDIF
-      csi = tmp(j+iesc-1:j+iesc+im-1)
-      ! we have a csi: we add new codes to it
-      IF (is_csi(csi)) THEN
-        csi = str_del_from_csi(csi,attrs)
-      ENDIF
-      csis=csis//csi//"|"
-      str = str//csi
-      j = j + iesc + im
-    ENDDO
-    ! Add <ESC>[0m at the end of string if not found
-    IF (INDEX(str,rcsi,.true.) /= LEN(str)-3) str = str//rcsi
-    ! resets all attributes if we only have <ESC>[0m in final list 
-    ok = tokenize(csis(1:LEN(csis)-1),tks,"|") 
-    IF (ALL(tks == rcsi)) str = str_reset_attributes(str)
-    DEALLOCATE(tks)
-    RETURN 
-  END FUNCTION str_delete_attributes
-
-  FUNCTION str_reset_attributes(string) RESULT(str)
-    !! Reset all csi codes of the string
-    !! 
-    !! The method removes __all__ the known escape sequences from the input string.
-    CHARACTER(len=*), INTENT(in) :: string
-      !! Input string
-    CHARACTER(len=:), ALLOCATABLE :: str 
-      !! An allocatable string with the copy of input string stripped off csi codes.
-    INTEGER :: j,iesc,im
-    LOGICAL :: tcsi
-    str = ""
-    IF (LEN(string) == 0) RETURN 
-    j=1 
-    DO 
-      IF (j>LEN(string)) EXIT
-      ! search for escape
-      iesc = INDEX(string(j:),CHAR(27))
-      IF (iesc == 0) THEN
-        str = str//string(j:) ; EXIT
-      ENDIF
-      ! search for m
-      im = INDEX(string(j+iesc:),"m")
-      ! no m in the string after ESC --> copy string (INCLUDING ESC) and leave
-      IF (im == 0) THEN
-        str = str//string(j+iesc-1:)
-        RETURN
-      ENDIF
-      ! csi includes everything between ESC and m (excluding them):
-      ! to check for csi it should begin by [ and then be a list of integers
-      ! separated by ;
-      tcsi = is_csi(string(j+iesc-1:j+iesc+im-1))
-      IF (iesc > 1) THEN
-        str = str//string(j:j+iesc-2)
-      ENDIF
-      j = j + iesc ; IF (tcsi) j=j+im
-    ENDDO
-    RETURN 
-  END FUNCTION str_reset_attributes
-
-
-  FUNCTION is_csi(value) RESULT(yes)
-    !! Check if string is a known csi
-    !! 
-    !! The function only check for known csi code which are defined in [[strings(module):attributes(variable)]].
-    CHARACTER(len=*), INTENT(in) :: value
-      !! A Fortran intrinsic string to check
-    LOGICAL :: yes
-      !! .true. if it is a known csi, .false. otherwise
-    LOGICAL                                           :: ok
-    CHARACTER(len=:), ALLOCATABLE                     :: tmp 
-    TYPE(words)                                       :: wtks
-    CHARACTER(len=st_slen), DIMENSION(:), ALLOCATABLE :: stks
-    INTEGER, DIMENSION(:), ALLOCATABLE                :: nums
-    INTEGER                                           :: i
-    yes = .false.
-    IF (LEN(value) < 4) RETURN
-    tmp = value(3:len(value)-1)
-    wtks = new_words(tmp,";")
-    ok = words_to_vector(wtks,stks)
-    CALL ws_clear_sc(wtks)
-    IF (.NOT.ok) RETURN
-    ! if we cannot convert strings to integers : it is not a csi
-    IF (.NOT.from_string(stks,nums)) RETURN
-    DEALLOCATE(stks)
-    DO i=1, SIZE(nums)
-      IF (.NOT.ANY(attributes == nums(i))) RETURN
-    ENDDO
-    yes = .true.
-  END FUNCTION is_csi
-
-  FUNCTION str_add_to_csi(csi,list) RESULT(ncsi)
-    !! Add a new list of codes to the input csi string
-    !! 
-    !! The method adds all the csi codes given in __list__ that are known by the module and not
-    !! already present in the input csi.
-    CHARACTER(len=*), INTENT(in)      :: csi
-      !! A string with the input csi. It __must__ begin with "<ESC>[" and ends with "m".
-    INTEGER, INTENT(in), DIMENSION(:) :: list
-      !! A vector of integers with the csi code to add. Each value of __list__ should be one of 
-      !! [[strings(module):attributes(variable)]] values. All unknwon values are filtered out as well 
-      !! as csi code already present in input __csi__.
-    CHARACTER(len=:), ALLOCATABLE :: ncsi 
-      !! A new csi string or the input __csi__ if some "errors" occured (the input csi could not 
-      !! be tokenized or none of __list__ values are left after filtering).
-    LOGICAL                                            :: ok 
-    CHARACTER(len=LEN(csi)), DIMENSION(:), ALLOCATABLE :: tks
-    CHARACTER(len=:), ALLOCATABLE                      :: tmp
-    INTEGER, DIMENSION(:), ALLOCATABLE                 :: zlist,nums
-    INTEGER                                            :: i,j,ni,no
-    ! 1) Filter input list :
-    ! 1.1) Gets the list of current csi codes 
-    ncsi = csi(3:len(csi)-1) 
-    ok = tokenize(ncsi,tks,"; ",merge=.true.)
-    IF (.NOT.from_string(tks,nums)) THEN
-      ncsi = csi
-      RETURN
-    ENDIF
-    DEALLOCATE(tks)
-    ! 1.2) Filter input list of new flags to add 
-    ! counts number of valid flags
-    j=0 
-    DO i=1,SIZE(list) 
-      ! new flags must be in attributes but NOT in nums
-      IF (ANY(attributes==list(i).AND..NOT.ANY(nums == list(i)))) j=j+1
-    ENDDO
-    ! No "valid" flags -> returns old csi
-    IF (j == 0) THEN ; ncsi = csi ; RETURN ; ENDIF
-    ni = SIZE(nums) ; no = j + ni
-    ALLOCATE(zlist(no)) ; zlist(1:ni) = nums(:) ; j = ni
-    DO i=1,SIZE(list) 
-      ! new flags must be in attributes but NOT in nums
-      IF (ANY(attributes==list(i).AND..NOT.ANY(nums == list(i)))) THEN
-        j=j+1 ; zlist(j) = list(i)
-      ENDIF 
-    ENDDO
-    DEALLOCATE(nums)
-    ! 2) Builds new csi
-    !    Here we explictly set the first flag to 0 (i.e. reset attributes)...
-    ncsi = CHAR(27)//"[0;"
-    DO i=1,no
-      ! ... So we get rid of all "0" flag in the list 
-      IF (zlist(i) /= 0) THEN
-        tmp = to_string(zlist(i))
-        IF (LEN_TRIM(tmp) == 0) THEN
-          ncsi = csi ; RETURN
-        ENDIF
-        ncsi = ncsi//tmp
-        IF (i /= no) ncsi = ncsi//";"
-      ENDIF
-    ENDDO
-    ncsi = ncsi//"m" 
-  END FUNCTION str_add_to_csi
-
-  FUNCTION str_del_from_csi(csi,list) RESULT(ncsi)
-    !! Remove a list of codes from the input csi string
-    !!
-    !! The method removes all the csi codes given in __list__ that are known by the
-    !! module and already present in the input csi.
-    CHARACTER(len=*), INTENT(in)      :: csi
-      !! An intrinsic Fortran string with the input csi. It __must__ begin with "<ESC>[" and ends with "m".
-    INTEGER, INTENT(in), DIMENSION(:) :: list
-      !! A vector of integers with the csi code to remove. Each value of __list__ should be one of 
-      !! [[strings(module):attributes(variable)]] values. All unknwon values are filtered out.
-    CHARACTER(len=:), ALLOCATABLE :: ncsi 
-      !! A new csi string or the input __csi__ if some "errors" occured (the input csi could not 
-      !! be tokenized or none of __list__ values are left after filtering).
-    LOGICAL                                            :: ok
-    CHARACTER(len=LEN(csi)), DIMENSION(:), ALLOCATABLE :: tks
-    CHARACTER(len=:), ALLOCATABLE                      :: tmp
-    INTEGER, DIMENSION(:), ALLOCATABLE                 :: nums
-    INTEGER                                            :: i
-    ncsi = csi(3:len(csi)-1) 
-    ok = tokenize(ncsi,tks,"; ",merge=.true.) 
-    IF (.NOT.from_string(tks,nums)) THEN 
-      ncsi = csi 
-      RETURN 
-    ENDIF
-    DEALLOCATE(tks)
-    tmp=""
-    DO i=1, SIZE(nums)
-      IF (ALL(nums(i) /= list).AND.nums(i) /= 0) THEN
-        ! no need to check for to_string status : it is always ok !
-        tmp=tmp//to_string(nums(i))//";"
-      ENDIF
-    ENDDO
-    IF (LEN_TRIM(tmp) /= 0) THEN
-      ncsi=CHAR(27)//"[0;"//tmp(1:LEN(tmp)-1)//"m"
-    ELSE
-      ncsi=CHAR(27)//"[0m"
-    ENDIF
-  END FUNCTION str_del_from_csi
-
-  FUNCTION str_index_of_csi(str,length) RESULT(pos)
-    !! Get the position of the first known csi in string
-    !!
-    !! The method searches for the first known csi in string. The csi must contain known codes 
-    !! (i.e. values of [[strings(module):attributes(variable)]]).
-    CHARACTER(len=*), INTENT(in) :: str    !! A string to search in
-    INTEGER, INTENT(out)         :: length !! Length of the csi in the string
-    INTEGER                      :: pos    !! Position of the first csi found. It is set to 0 if no csi has been found.
-    INTEGER :: iesc,im
-    pos = 0 ; length = 0
-    ! we need at least 4 chars to create a csi
-    IF (LEN_TRIM(str) < 4) RETURN 
-    iesc = INDEX(str,CHAR(27))
-    IF (iesc == 0) RETURN
-    ! search for m
-    im = INDEX(str(iesc:),"m")
-    ! no m in the string after ESC --> copy string (INCLUDING ESC) and leave
-    IF (im == 0) RETURN
-    IF (.NOT.is_csi(str(iesc:iesc+im-1))) RETURN
-    pos = iesc ; length = im
-  END FUNCTION str_index_of_csi
-
-  ! String conversion functions
-  ! ---------------------------
-
-  FUNCTION str2int_sc(str, value) RESULT(ret)
-    !! Convert string value to integer value (scalar)
-    CHARACTER(len=*), INTENT(in) :: str   !! String to convert
-    INTEGER, INTENT(out)         :: value !! Output value
-    LOGICAL :: ret                        !! Return status (.true. on success)
-    CHARACTER(len=:), ALLOCATABLE :: zs
-    ret = .true. ; zs = remove_quotes(str)
-    IF (string_is(zs) /= st_integer) THEN
-      ret = .false.
-    ELSE
-      READ(zs, *) value
-    ENDIF
-    RETURN
-  END FUNCTION str2int_sc
-
-  FUNCTION str2log_sc(str, value) RESULT(ret)
-    !! Convert string value to logical value (scalar)
-    CHARACTER(len=*), INTENT(in) :: str   !! String to convert
-    LOGICAL, INTENT(out)         :: value !! Output value
-    LOGICAL :: ret                        !! Return status (.true. on success)
-    CHARACTER(len=:), ALLOCATABLE :: zs
-    integer :: r
-    ret = .true. ; zs = remove_quotes(str)
-    r = string_is(zs)
-    IF (string_is(zs) /= st_logical) THEN
-      ret = .false.
-    ELSE
-      READ(zs, *) value
-    ENDIF
-    RETURN
-  END FUNCTION str2log_sc
-
-  FUNCTION str2real_sc(str, value) RESULT(ret)
-    !! Convert string value to simple precision floating precision value (scalar)
-    CHARACTER(len=*), INTENT(in) :: str   !! String to convert
-    REAL(kind=4), INTENT(out)    :: value !! Output value
-    LOGICAL :: ret                        !! Return status (.true. on success)
-    CHARACTER(len=:), ALLOCATABLE :: zs
-    ret = .true.; zs = remove_quotes(str)
-    IF (string_is(zs) < st_integer) THEN
-      ret = .false.
-    ELSE
-      READ(zs, *) value
-    ENDIF
-    RETURN
-  END FUNCTION str2real_sc
-
-  FUNCTION str2dble_sc(str, value) RESULT(ret)
-    !! Convert string value to double precision floating precision value (scalar)
-    CHARACTER(len=*), INTENT(in) :: str   !! String to convert
-    REAL(kind=8), INTENT(out)    :: value !! Output value
-    LOGICAL :: ret                        !! Return status (.true. on success)
-    CHARACTER(len=:), ALLOCATABLE :: zs
-    ret = .true. ; zs = remove_quotes(str)
-    IF (string_is(zs) < st_integer) THEN
-      ret = .false.
-    ELSE
-      READ(zs, *) value
-    ENDIF
-    RETURN
-  END FUNCTION str2dble_sc
-
-  FUNCTION str2cplx_sc(str, value) RESULT(ret)
-    !! Convert string value to complex value (scalar)
-    CHARACTER(len=*), INTENT(in) :: str   !! String to convert
-    COMPLEX(kind=4), INTENT(out) :: value !! Output value
-    LOGICAL :: ret                        !! Return status (.true. on success)
-    ! - LOCAL 
-    CHARACTER(len=:), ALLOCATABLE :: zs
-    ret = .true. ; zs = remove_quotes(str)
-    IF (string_is(zs) /= st_complex) THEN
-      ret = .false.
-    ELSE
-      READ(zs, *) value
-    ENDIF
-    RETURN
-  END FUNCTION str2cplx_sc
-
-  FUNCTION str2int_ve(str, value) RESULT(ret)
-    !! Convert strings values to integer values (vector)
-    CHARACTER(len=*), INTENT(in), DIMENSION(:)      :: str   !! Vector of strings to convert
-    INTEGER, INTENT(out), DIMENSION(:), ALLOCATABLE :: value !! Vector of output values
-    LOGICAL :: ret                                           !! Return status (.true. on success)
-    INTEGER                       :: i,ns
-    CHARACTER(len=:), ALLOCATABLE :: zs
-    ret = .true. ; ns = SIZE(str) ; ALLOCATE(value(ns))
-    DO i=1,ns
-      zs = remove_quotes(str(i))
-      IF (string_is(zs) /= st_integer) THEN
-        ret = .false. ; DEALLOCATE(value) ; RETURN
-      ELSE
-        READ(zs, *) value(i)
-      ENDIF
-    ENDDO
-    RETURN
-  END FUNCTION str2int_ve
-
-  FUNCTION str2log_ve(str, value) RESULT(ret)
-    !! Convert strings values to logical values (vector)
-    CHARACTER(len=*), INTENT(in), DIMENSION(:)      :: str   !! Vector of strings to convert
-    LOGICAL, INTENT(out), DIMENSION(:), ALLOCATABLE :: value !! Vector of output values
-    LOGICAL :: ret                                           !! Return status (.true. on success)
-    INTEGER                       :: i,ns
-    CHARACTER(len=:), ALLOCATABLE :: zs
-    ret = .true. ; ns = SIZE(str) ; ALLOCATE(value(ns))
-    DO i=1,ns
-      zs = remove_quotes(str(i))
-      IF (string_is(zs) /= st_logical) THEN
-        ret = .false. ; DEALLOCATE(value) ; RETURN
-      ELSE
-        READ(zs, *) value(i)
-      ENDIF
-    ENDDO
-    RETURN
-  END FUNCTION str2log_ve
-
-  FUNCTION str2real_ve(str, value) RESULT(ret)
-    !! Convert strings values to simple precision floating point values (vector)
-    CHARACTER(len=*), INTENT(in), DIMENSION(:)           :: str   !! Vector of strings to convert
-    REAL(kind=4), INTENT(out), DIMENSION(:), ALLOCATABLE :: value !! Vector of output values
-    LOGICAL :: ret                                                !! Return status (.true. on success) 
-    INTEGER                       :: i,ns
-    CHARACTER(len=:), ALLOCATABLE :: zs
-    ret = .true. ; ns = SIZE(str) ; ALLOCATE(value(ns))
-    DO i=1,ns
-      IF (string_is(zs) < st_integer) THEN
-        ret = .false. ; DEALLOCATE(value) ; RETURN
-      ELSE
-        READ(zs, *) value(i)
-      ENDIF
-    ENDDO
-    RETURN
-  END FUNCTION str2real_ve
-
-  FUNCTION str2dble_ve(str, value) RESULT(ret)
-    !! Convert strings values to double precision floating point values (vector)
-    CHARACTER(len=*), INTENT(in), DIMENSION(:)           :: str   !! Vector of strings to convert
-    REAL(kind=8), INTENT(out), DIMENSION(:), ALLOCATABLE :: value !! Vector of output values
-    LOGICAL :: ret                                                !! Return status (.true. on success)
-    INTEGER                       :: i,ns
-    CHARACTER(len=:), ALLOCATABLE :: zs
-    ret = .true. ; ns = SIZE(str) ; ALLOCATE(value(ns))
-    DO i=1,ns
-      zs = remove_quotes(str(i))
-      IF (string_is(zs) < st_integer) THEN
-        ret = .false. ; DEALLOCATE(value) ; RETURN
-      ELSE
-        READ(zs, *) value(i)
-      ENDIF
-    ENDDO
-    RETURN
-  END FUNCTION str2dble_ve
-
-  FUNCTION str2cplx_ve(str, value) RESULT(ret)
-    !! Convert strings values to complex values (vector)
-    CHARACTER(len=*), INTENT(in), DIMENSION(:)              :: str   !! Vector of strings to convert
-    COMPLEX(kind=4), INTENT(out), DIMENSION(:), ALLOCATABLE :: value !! Vector of output values
-    LOGICAL :: ret                                                   !! Return status (.true. on success)
-    INTEGER                       :: i,ns
-    CHARACTER(len=:), ALLOCATABLE :: zs
-    ret = .true. ; ns = SIZE(str) ; ALLOCATE(value(ns))
-    DO i=1,ns
-      zs = remove_quotes(str(i))
-      IF (string_is(zs) /= st_complex) THEN
-        ret = .false. ; DEALLOCATE(value) ; RETURN
-      ELSE
-        READ(zs, *) value(i)
-      ENDIF
-    ENDDO
-    RETURN
-  END FUNCTION str2cplx_ve
-
-  FUNCTION int2str_as(value) RESULT(str)
-    !! Convert an integer value to string (auto format / string result) 
-    INTEGER, INTENT(in)           :: value !! Value to convert
-    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
-    INTEGER :: err
-    ALLOCATE(CHARACTER(len=DIGITS(value)) :: str)
-    WRITE(str,*,iostat=err) value
-    str = TRIM(ADJUSTL(str))
-    IF (err /= 0) str = '' 
-    RETURN
-  END FUNCTION int2str_as
-
-  FUNCTION log2str_as(value) RESULT(str)
-    !! Convert a logical value to string (auto format / string result) 
-    LOGICAL, INTENT(in)           :: value !! Value to convert
-    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
-    INTEGER :: err
-    ALLOCATE(CHARACTER(len=2) :: str)
-    WRITE(str, *, IOSTAT = err) value
-    str=TRIM(ADJUSTL(str))
-    IF (err /= 0) str = ''
-    RETURN
-  END FUNCTION log2str_as
-
-  FUNCTION real2str_as(value) RESULT(str)
-    !! Convert a simple precision floating point value to string (auto format / string result) 
-    REAL(kind=4), INTENT(in)      :: value !! Value to convert
-    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
-    INTEGER :: err
-    ALLOCATE(CHARACTER(len=DIGITS(value)) ::str)
-    WRITE(str,*, IOSTAT = err) value
-    str=TRIM(ADJUSTL(str))
-    IF (err /= 0)  str = '' 
-    RETURN
-  END FUNCTION real2str_as
-
-  FUNCTION dble2str_as(value) RESULT(str)
-    !! Convert a double precision floating point value to string (auto format / string result) 
-    REAL(kind=8), INTENT(in)      :: value !! Value to convert
-    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
-    INTEGER :: err
-    ALLOCATE(CHARACTER(len=DIGITS(value)) ::str)
-    WRITE(str,*, IOSTAT = err) value
-    str=TRIM(ADJUSTL(str))
-    IF (err /= 0) str = '' 
-    RETURN
-  END FUNCTION dble2str_as
-
-  FUNCTION cplx2str_as(value) RESULT(str)
-    !! Convert a complex value to string (auto format / string result) 
-    COMPLEX(kind=4), INTENT(in)   :: value !! Value to convert
-    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
-    INTEGER :: err,sl
-    sl = DIGITS(REAL(value))*2+3
-    ALLOCATE(CHARACTER(len=sl) :: str)
-    WRITE(str, *, IOSTAT = err) value
-    str = TRIM(ADJUSTL(str))
-    IF (err /= 0) str = '' 
-    RETURN
-  END FUNCTION cplx2str_as
-
-  FUNCTION int2str_fs(value, fmt, width) RESULT(str)
-    !! Convert an integer value to string (user format / string result) 
-    INTEGER, INTENT(in)           :: value !! Value to convert
-    CHARACTER(len=*), INTENT(in)  :: fmt   !! String format
-    INTEGER, INTENT(in)           :: width !! Expected width of the output string (as defined in __fmt__)
-    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
-    INTEGER :: err
-    ALLOCATE(CHARACTER(len=width) :: str)
-    WRITE(str, '('//fmt//')', IOSTAT = err) value
-    str = TRIM(ADJUSTL(str))
-    IF (err /= 0) str = '' 
-    RETURN
-  END FUNCTION int2str_fs
-
-  FUNCTION log2str_fs(value, fmt, width) RESULT(str)
-    !! Convert a logical value to string (user format / string result) 
-    LOGICAL, INTENT(in)           :: value !! Value to convert
-    CHARACTER(len=*), INTENT(in)  :: fmt   !! String format
-    INTEGER, INTENT(in)           :: width !! Expected width of the output string (as defined in __fmt__)
-    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
-    INTEGER :: err
-    ALLOCATE(CHARACTER(len=width) :: str)
-    WRITE(str, '('//fmt//')', IOSTAT = err) value
-    str=TRIM(ADJUSTL(str))
-    IF (err /= 0) str = '' 
-    RETURN
-  END FUNCTION log2str_fs
-
-  FUNCTION real2str_fs(value, fmt, width) RESULT(str)
-    !! Convert a simple precision floating point value to string (user format / string result) 
-    REAL(kind=4), INTENT(in)      :: value !! Value to convert
-    CHARACTER(len=*), INTENT(in)  :: fmt   !! String format
-    INTEGER, INTENT(in)           :: width !! Expected width of the output string (as defined in __fmt__)
-    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
-    INTEGER :: err
-    ALLOCATE(CHARACTER(len=width) :: str)
-    WRITE(str, '('//fmt//')', IOSTAT = err) value
-    str = TRIM(ADJUSTL(str))
-    IF (err /= 0) str = '' 
-    RETURN
-  END FUNCTION real2str_fs
-
-  FUNCTION dble2str_fs(value, fmt, width) RESULT(str)
-    !! Convert a double precision floating point value to string (user format / string result) 
-    REAL(kind=8), INTENT(in)      :: value !! Value to convert
-    CHARACTER(len=*), INTENT(in)  :: fmt   !! String format
-    INTEGER, INTENT(in)           :: width !! Expected width of the output string (as defined in __fmt__)
-    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
-    INTEGER :: err
-    ALLOCATE(CHARACTER(len=width) :: str)
-    WRITE(str, '('//fmt//')', IOSTAT = err) value
-    str = TRIM(ADJUSTL(str))
-    IF (err /= 0) str = '' 
-    RETURN
-  END FUNCTION dble2str_fs
-
-  FUNCTION cplx2str_fs(value, fmt, width) RESULT(str)
-    !! Convert a complex value to string (user format / string result) 
-    COMPLEX(kind=4), INTENT(in)   :: value !! Value to convert
-    CHARACTER(len=*), INTENT(in)  :: fmt   !! String format
-    INTEGER, INTENT(in)           :: width !! Expected width of the output string (as defined in __fmt__)
-    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
-    INTEGER :: err
-    ALLOCATE(CHARACTER(len=width) :: str)
-    WRITE(str, '('//fmt//')', IOSTAT = err) value
-    str = TRIM(ADJUSTL(str))
-    IF (err /= 0) str = '' 
-    RETURN
-  END FUNCTION cplx2str_fs
-
-END MODULE STRINGS
-
-MODULE ESTRINGS
-  !! Fortran strings extensions
-  !!
-  !! This module is an extension of [[strings(module)]] module (i.e. contains all its definitions).
-  !! It defines overloaded string concatenation (//) and assignment operators (=) that simplify the 
-  !! conversion between intrinsic types and strings.
-  !!
-  !! These operators only work with allocatable strings.
-  USE STRINGS
-
-  PUBLIC
-
-  PRIVATE :: str_affect_int, str_affect_bool, str_affect_real,        &
-             str_affect_double, str_affect_cplx, str_affect_dcplx,    &
-             str_cat_int, str_cat_bool, str_cat_real, str_cat_double, &
-             str_cat_cplx, str_cat_dcplx, str_cat_int_inv,            &
-             str_cat_bool_inv, str_cat_real_inv, str_cat_double_inv,  &
-             str_cat_cplx_inv, str_cat_dcplx_inv
-
-
-  
-  !> Overloaded string assignment operator interface
-  INTERFACE ASSIGNMENT(=)
-    MODULE PROCEDURE str_affect_int, str_affect_bool, str_affect_real,    &
-                     str_affect_double, str_affect_cplx, str_affect_dcplx
-  END INTERFACE
-
-  !> Overloaded string concatentation operator interface
-  INTERFACE OPERATOR(//)
-    MODULE PROCEDURE str_cat_int, str_cat_bool, str_cat_real, str_cat_double, &
-                     str_cat_cplx, str_cat_dcplx
-    MODULE PROCEDURE str_cat_int_inv, str_cat_bool_inv, str_cat_real_inv,     &
-                     str_cat_double_inv, str_cat_cplx_inv, str_cat_dcplx_inv
-  END INTERFACE
-
-
-  CONTAINS
-
-  ! Extended strings features
-  ! ---------------------------
-
-  FUNCTION str_cat_int(str1,int2) RESULT(str)
-    !! Concatenate a string with a integer
-    CHARACTER(len=*), INTENT(in)  :: str1 !! String to concatenate
-    INTEGER, INTENT(in)           :: int2 !! Integer to concatenate
-    CHARACTER(len=:), ALLOCATABLE :: str  !! Concatenation resulting string
-    ALLOCATE(CHARACTER(len=DIGITS(int2)) :: str) 
-    WRITE(str,*) int2 ; str = TRIM(ADJUSTL(str))
-    IF (LEN(str1) /= 0) str = str1//str
-    RETURN
-  END FUNCTION str_cat_int
-
-  !! @param[in] int2 An integer to concatenate
-  !! @param[in] str1 A string to concatenate
-  !! @return An allocatable string with the concatenation of input values.
-  FUNCTION str_cat_int_inv(int2,str1) RESULT(str)
-    !! Concatenate a string with a integer (inversed)
-    INTEGER, INTENT(in)           :: int2 !! Integer to concatenate 
-    CHARACTER(len=*), INTENT(in)  :: str1 !! String to concatenate
-    CHARACTER(len=:), ALLOCATABLE :: str  !! Concatenation resulting string
-    ALLOCATE(CHARACTER(len=DIGITS(int2)) :: str) 
-    WRITE(str,*) int2 ; str = TRIM(ADJUSTL(str))
-    IF (LEN(str1) /= 0) str = str//str1
-    RETURN
-  END FUNCTION str_cat_int_inv
-
-  FUNCTION str_cat_bool(str1,bool2) RESULT(str)
-    !! Concatenate a string with a logical
-    CHARACTER(len=*), INTENT(in)  :: str1  !! String to concatenate
-    LOGICAL, INTENT(in)           :: bool2 !! Logical to concatenate
-    CHARACTER(len=:), ALLOCATABLE :: str   !! Concatenation resulting string
-    CHARACTER(len=2) :: tmp 
-    WRITE(tmp,*) bool2 
-    str=TRIM(ADJUSTL(tmp))
-    IF (LEN(str1) /= 0) str = str1//str
-    RETURN
-  END FUNCTION str_cat_bool
-
-  FUNCTION str_cat_bool_inv(bool2,str1) RESULT(str)
-    !! Concatenate a string with a logical (inversed)
-    LOGICAL, INTENT(in)           :: bool2 !! Logical to concatenate
-    CHARACTER(len=*), INTENT(in)  :: str1  !! String to concatenate
-    CHARACTER(len=:), ALLOCATABLE :: str   !! Concatenation resulting string
-    CHARACTER(len=2) :: tmp 
-    WRITE(tmp,*) bool2 
-    str = TRIM(ADJUSTL(tmp))
-    IF (LEN(str1) /= 0) str = str//str1
-    RETURN
-  END FUNCTION str_cat_bool_inv
-
-  FUNCTION str_cat_real(str1,real2) RESULT(str)
-    !! Concatenate a string with a real simple precision
-    CHARACTER(len=*), INTENT(in)  :: str1  !! String to concatenate
-    REAL(kind=4), INTENT(in)      :: real2 !! Simple precision real to concatenate
-    CHARACTER(len=:), ALLOCATABLE :: str   !! Concatenation resulting string
-    ALLOCATE(CHARACTER(len=DIGITS(real2)) :: str) 
-    WRITE(str,*) real2 ; str = TRIM(ADJUSTL(str))
-    IF (LEN(str1) /= 0) str=str1//str 
-    RETURN
-  END FUNCTION str_cat_real
-
-  FUNCTION str_cat_real_inv(real2,str1) RESULT(str)
-    !! Concatenate a string with a real simple precision (inversed)
-    REAL(kind=4), INTENT(in)      :: real2 !! Simple precision real to concatenate
-    CHARACTER(len=*), INTENT(in)  :: str1  !! String to concatenate
-    CHARACTER(len=:), ALLOCATABLE :: str   !! Concatenation resulting string
-    ALLOCATE(CHARACTER(len=DIGITS(real2)) :: str) 
-    WRITE(str,*) real2  ; str = TRIM(ADJUSTL(str))
-    IF (LEN(str1) /= 0) str = str//str1
-    RETURN
-  END FUNCTION str_cat_real_inv
-
-  FUNCTION str_cat_double(str1,double2) RESULT(str)
-    !! Concatenate a string with a real double precision
-    CHARACTER(len=*), INTENT(in)  :: str1    !! String to concatenate
-    REAL(kind=8), INTENT(in)      :: double2 !! Double precision real to concatenate
-    CHARACTER(len=:), ALLOCATABLE :: str     !! Concatenation resulting string
-    ALLOCATE(CHARACTER(len=DIGITS(double2)) :: str) 
-    WRITE(str,*) double2 ; str = TRIM(ADJUSTL(str))
-    IF (LEN(str1) /= 0) str=str1//str 
-    RETURN
-  END FUNCTION str_cat_double
-
-  FUNCTION str_cat_double_inv(double2,str1) RESULT(str)
-    !! Concatenate a string with a real double precision (inversed)
-    REAL(kind=8), INTENT(in)      :: double2 !! Double precision real to concatenate
-    CHARACTER(len=*), INTENT(in)  :: str1    !! String to concatenate
-    CHARACTER(len=:), ALLOCATABLE :: str     !! Concatenation resulting string
-    ALLOCATE(CHARACTER(len=DIGITS(double2)) :: str) 
-    WRITE(str,*) double2  ; str = TRIM(ADJUSTL(str))
-    IF (LEN(str1) /= 0) str = str//str1
-    RETURN
-  END FUNCTION str_cat_double_inv
-
-  FUNCTION str_cat_cplx(str1,cplx2) RESULT(str)
-    !! Concatenate a string with a complex 
-    CHARACTER(len=*), INTENT(in)  :: str1  !! String to concatenate 
-    COMPLEX(kind=4), INTENT(in)   :: cplx2 !! Complex value to concatenate 
-    CHARACTER(len=:), ALLOCATABLE :: str   !! Concatenation resulting string
-    INTEGER :: sl
-    sl = DIGITS(REAL(cplx2))*2+3
-    ALLOCATE(CHARACTER(len=sl) :: str)
-    WRITE(str,*) cplx2 ; str = TRIM(ADJUSTL(str))
-    IF (LEN(str1) /= 0) str = str//str1
-    RETURN
-  END FUNCTION str_cat_cplx
-
-  FUNCTION str_cat_cplx_inv(cplx2,str1) RESULT(str)
-    !! Concatenate a string with a complex (inversed)
-    COMPLEX(kind=4), INTENT(in)   :: cplx2 !! Complex value to concatenate 
-    CHARACTER(len=*), INTENT(in)  :: str1  !! String to concatenate 
-    CHARACTER(len=:), ALLOCATABLE :: str   !! Concatenation resulting string
-    INTEGER :: sl
-    sl = DIGITS(REAL(cplx2))*2+3
-    ALLOCATE(CHARACTER(len=sl) :: str)
-    WRITE(str,*) cplx2
-    str = TRIM(ADJUSTL(str))
-    IF (LEN(str1) /= 0) str = str//str1
-    RETURN
-  END FUNCTION str_cat_cplx_inv
-
-  FUNCTION str_cat_dcplx(str1,dcplx2) RESULT(str)
-    !! Concatenate a string with a double precision complex 
-    CHARACTER(len=*), INTENT(in)  :: str1   !! String to concatenate
-    COMPLEX(kind=8), INTENT(in)   :: dcplx2 !! Complex value to concatenate 
-    CHARACTER(len=:), ALLOCATABLE :: str    !! Concatenation resulting string
-    INTEGER :: sl
-    sl = DIGITS(REAL(dcplx2))*2+3
-    ALLOCATE(CHARACTER(len=sl) :: str)
-    WRITE(str,*) dcplx2 ; str = TRIM(ADJUSTL(str))
-    IF (LEN(str1) /= 0) str = str//str1
-    RETURN
-  END FUNCTION str_cat_dcplx
-
-  FUNCTION str_cat_dcplx_inv(dcplx2,str1) RESULT(str)
-    !! Concatenate a string with a double precision complex (inversed)
-    COMPLEX(kind=8), INTENT(in)   :: dcplx2 !! Complex value to concatenate 
-    CHARACTER(len=*), INTENT(in)  :: str1   !! string to concatenate
-    CHARACTER(len=:), ALLOCATABLE :: str    !! Concatenation resulting string
-    INTEGER :: sl
-    sl = DIGITS(REAL(dcplx2))*2+3
-    ALLOCATE(CHARACTER(len=sl) :: str)
-    WRITE(str,*) dcplx2
-    str = TRIM(ADJUSTL(str))
-    IF (LEN(str1) /= 0) str = str//str1
-    RETURN
-  END FUNCTION str_cat_dcplx_inv
-
-  SUBROUTINE str_affect_int(str,int)
-    !! Assignment subroutine (using intrinsic integer) 
-    CHARACTER(len=:), INTENT(out), ALLOCATABLE :: str !! Output string to be assigned
-    INTEGER, INTENT(in)                        :: int !! Input value to assign 
-    str = str_cat_int('',int)
-  END SUBROUTINE str_affect_int
-
-  SUBROUTINE str_affect_bool(str,bool)
-    !! Assignment subroutine (using intrinsic logical) 
-    CHARACTER(len=:), INTENT(out), ALLOCATABLE :: str  !! Output string to be assigned
-    LOGICAL, INTENT(in)                        :: bool !! Input value to assign
-    str = str_cat_bool('',bool)
-  END SUBROUTINE str_affect_bool
-
-  SUBROUTINE str_affect_real(str,float)
-    !! Assignment subroutine (using intrinsic real) 
-    CHARACTER(len=:), INTENT(out), ALLOCATABLE :: str   !! Output string to be assigned
-    REAL(kind=4), INTENT(in)                   :: float !! Input value to assign
-    str = str_cat_real('',float)
-  END SUBROUTINE str_affect_real
-
-  SUBROUTINE str_affect_double(str,double)
-    !! Assignment subroutine (using intrinsic real(kind=8)) 
-    CHARACTER(len=:), INTENT(out), ALLOCATABLE :: str    !! Output string to be assigned
-    REAL(kind=8), INTENT(in)                   :: double !! Input value to assign 
-    str = str_cat_double('',double)
-  END SUBROUTINE str_affect_double
-
-  SUBROUTINE str_affect_cplx(str,cplx)
-    !! Assignment subroutine (using intrinsic complex) 
-    CHARACTER(len=:), INTENT(out), ALLOCATABLE :: str  !! Output string to be assigned
-    COMPLEX(kind=4), INTENT(in)                        :: cplx !! Input value to assign
-    str = str_cat_cplx('',cplx)
-  END SUBROUTINE str_affect_cplx
-
-  SUBROUTINE str_affect_dcplx(str,dcplx)
-    !! Assignment subroutine (using intrinsic complex(kind=8)) 
-    CHARACTER(len=:), INTENT(out), ALLOCATABLE :: str   !! Output string to be assigned
-    COMPLEX(kind=8), INTENT(in)                :: dcplx !! Input value to assign
-    str = str_cat_dcplx('',dcplx)
-  END SUBROUTINE str_affect_dcplx
-
-END MODULE estrings
Index: trunk/LMDZ.TITAN/libf/phytitan/callcorrk.F90
===================================================================
--- trunk/LMDZ.TITAN/libf/phytitan/callcorrk.F90	(revision 1896)
+++ trunk/LMDZ.TITAN/libf/phytitan/callcorrk.F90	(revision 1897)
@@ -45,5 +45,5 @@
       INTEGER,INTENT(IN) :: ngrid                  ! Number of atmospheric columns.
       INTEGER,INTENT(IN) :: nlayer                 ! Number of atmospheric layers.
-      REAL,INTENT(IN) :: pq(ngrid,nlayer,nq)       ! Tracers (kg/kg_of_air).
+      REAL,INTENT(IN) :: pq(ngrid,nlayer,nq)       ! Tracers (X/m2).
       INTEGER,INTENT(IN) :: nq                     ! Number of tracers.
       REAL,INTENT(IN) :: qsurf(ngrid,nq)           ! Tracers on surface (kg.m-2).
@@ -298,5 +298,5 @@
             endif
             
-            call optcv(pq,plevrad,tmid,pmid,                       &
+            call optcv(pq(ig,:,1:nmicro),nlayer,plevrad,tmid,pmid,        &
                  dtauv,tauv,taucumv,wbarv,cosbv,tauray,taugsurf)
 
@@ -340,5 +340,5 @@
 !-----------------------------------------------------------------------
 
-         call optci(pq,plevrad,tlevrad,tmid,pmid,                   &
+         call optci(pq(ig,:,1:nmicro),nlayer,plevrad,tlevrad,tmid,pmid,    &
               dtaui,taucumi,cosbi,wbari,taugsurfi)
 
Index: trunk/LMDZ.TITAN/libf/phytitan/callkeys_mod.F90
===================================================================
--- trunk/LMDZ.TITAN/libf/phytitan/callkeys_mod.F90	(revision 1896)
+++ trunk/LMDZ.TITAN/libf/phytitan/callkeys_mod.F90	(revision 1897)
@@ -12,6 +12,9 @@
       logical,save :: callgasvis,continuum,graybody
 !$OMP THREADPRIVATE(callgasvis,continuum,graybody)
-      logical,save :: strictboundcorrk                                     
+      logical,save :: strictboundcorrk
 !$OMP THREADPRIVATE(strictboundcorrk)
+      logical,save :: uncoupl_optic_haze
+!$OMP THREADPRIVATE(uncoupl_optic_haze)
+
       logical,save :: callchim, callmufi, callclouds
 !$OMP THREADPRIVATE(callchim,callmufi,callclouds)
@@ -45,9 +48,8 @@
 !$OMP THREADPRIVATE(iddist,iradia,startype)
       
-      real,save :: df_mufi, rm_mufi, rho_aer_mufi
       real,save :: p_prod, tx_prod, rc_prod
       real,save :: air_rad
-!$OMP THREADPRIVATE(df_mufi, rm_mufi, rho_aer_mufi,p_prod,tx_prod,rc_prod,air_rad)
-
+!$OMP THREADPRIVATE(p_prod,tx_prod,rc_prod,air_rad)
+      
       real,save :: szangle
 !$OMP THREADPRIVATE(szangle)
Index: trunk/LMDZ.TITAN/libf/phytitan/calmufi.F90
===================================================================
--- trunk/LMDZ.TITAN/libf/phytitan/calmufi.F90	(revision 1896)
+++ trunk/LMDZ.TITAN/libf/phytitan/calmufi.F90	(revision 1897)
@@ -1,6 +1,8 @@
+
+
 SUBROUTINE calmufi(plev, zlev, play, zlay, temp, pq, zdq)
   !! Interface subroutine to YAMMS model for Titan LMDZ GCM.
   !!
-  !! The subroutine computes the microphysics processes for a single vertical column.
+  !! The subroutine computes the microphysics processes for a si:le vertical column.
   !!
   !! - All input vectors are assumed to be defined from GROUND to TOP of the atmosphere.
@@ -52,5 +54,4 @@
 
   INTEGER :: ilon, i,nices
-
   INTEGER :: nlon,nlay
 
@@ -89,6 +90,5 @@
     ! Convert tracers to extensive ( except for gazs where we work with molar mass ratio )
     ! We suppose a given order of tracers !
-    int2ext(:) = ( plev(ilon,1:nlay) - plev(ilon,2:nlay+1) ) / g
-
+    int2ext(:) = ( plev(ilon,1:nlay)-plev(ilon,2:nlay+1) ) / g
     m0as(:) = pq(ilon,:,1) * int2ext(:)
     m3as(:) = pq(ilon,:,2) * int2ext(:)
@@ -97,13 +97,12 @@
     
     if (callclouds) then ! if call clouds
-      dm0n(:) = pq(ilon,:,5) * int2ext(:)
-      dm3n(:) = pq(ilon,:,6) * int2ext(:)
+      m0n(:) = pq(ilon,:,5) * int2ext(:)
+      m3n(:) = pq(ilon,:,6) * int2ext(:)
       do i=1,nices
-        dm3i(:,nices) = pq(ilon,:,6+i) * int2ext(:)
-        dgazs(:,i)    = pq(ilon,:,ices_indx(i)) * rat_mmol(ices_indx(i)) ! For gazs we work on the full tracer array !!
+        m3i(:,nices) = pq(ilon,:,6+i) * int2ext(:)
+        gazs(:,i)    = pq(ilon,:,ices_indx(i)) * rat_mmol(ices_indx(i)) ! For gazs we work on the full tracer array !!
         ! We use the molar mass ratio from GCM in case there is discrepancy with the mm one
       enddo
     endif
-
 
     ! Initialize YAMMS atmospheric column
@@ -135,5 +134,5 @@
     ! Convert tracers back to intensives ( except for gazs where we work with molar mass ratio )
     ! We suppose a given order of tracers !
-
+ 
     zdq(ilon,:,1) = dm0as(:) / int2ext(:)
     zdq(ilon,:,2) = dm3as(:) / int2ext(:)
@@ -150,5 +149,4 @@
       enddo
     endif
-
   END DO ! loop on ilon
 
Index: trunk/LMDZ.TITAN/libf/phytitan/inifis_mod.F90
===================================================================
--- trunk/LMDZ.TITAN/libf/phytitan/inifis_mod.F90	(revision 1896)
+++ trunk/LMDZ.TITAN/libf/phytitan/inifis_mod.F90	(revision 1897)
@@ -377,18 +377,9 @@
      endif
 
-     write(*,*) "Fractal dimension ?"
-     df_mufi=2.0 ! default value
-     call getin_p("df_mufi",df_mufi)
-     write(*,*)" df_mufi = ",df_mufi
-
-     write(*,*) "Monomer radius (m) ?"
-     rm_mufi=6.66e-08 ! default value
-     call getin_p("rm_mufi",rm_mufi)
-     write(*,*)" rm_mufi = ",rm_mufi
-
-     write(*,*) "Aerosol density (kg.m-3)?"
-     rho_aer_mufi=1.e3 ! default value
-     call getin_p("rho_aer_mufi",rho_aer_mufi)
-     write(*,*)" rho_aer_mufi = ",rho_aer_mufi
+     write(*,*) "Disable the coupling of microphysics within rad. transf. ?"
+     write(*,*) "If disabled we will assume a planetwide vert. profile of extinction ..."
+     uncoupl_optic_haze=.true. ! default value - true as long as the microphysics is bugged
+     call getin_p("uncoupl_optic_haze",uncoupl_optic_haze)
+     write(*,*)" uncoupl_optic_haze = ",uncoupl_optic_haze
 
      write(*,*) "Pressure level of aer. production (Pa) ?"
Index: trunk/LMDZ.TITAN/libf/phytitan/inimufi.F90
===================================================================
--- trunk/LMDZ.TITAN/libf/phytitan/inimufi.F90	(revision 1896)
+++ trunk/LMDZ.TITAN/libf/phytitan/inimufi.F90	(revision 1897)
@@ -2,6 +2,5 @@
 
   use mmp_gcm
-  use callkeys_mod, only : callclouds, df_mufi, &
-        rm_mufi, rho_aer_mufi, p_prod, tx_prod, rc_prod, air_rad
+  use callkeys_mod, only : callclouds, p_prod, tx_prod, rc_prod, air_rad
   use tracer_h
   use comcstfi_mod, only : g, rad, mugaz
@@ -42,4 +41,7 @@
   logical :: err
 
+  ! PATCH : YAMMS now allows to enable/disable effective g computations:
+  !  In the library it defaults to .true., in the GCM we do not want it !
+  mm_use_effg = .false.
 
   !----------------------------------------------------
@@ -47,5 +49,5 @@
   ! ---------------------------------------------------
 
-   call mmp_initialize(ptimestep,df_mufi,rm_mufi,rho_aer_mufi,p_prod,tx_prod,rc_prod, &
+   call mmp_initialize(ptimestep,p_prod,tx_prod,rc_prod, &
         rad,g,air_rad,mugaz,callclouds,config_mufi)
 
Index: trunk/LMDZ.TITAN/libf/phytitan/optci.F90
===================================================================
--- trunk/LMDZ.TITAN/libf/phytitan/optci.F90	(revision 1896)
+++ trunk/LMDZ.TITAN/libf/phytitan/optci.F90	(revision 1897)
@@ -1,3 +1,3 @@
-subroutine optci(PQ,PLEV,TLEV,TMID,PMID,      &
+subroutine optci(PQO,NLAY,PLEV,TLEV,TMID,PMID,      &
      DTAUI,TAUCUMI,COSBI,WBARI,TAUGSURF)
 
@@ -6,5 +6,8 @@
   use gases_h
   use comcstfi_mod, only: g, r
-  use callkeys_mod, only: continuum,graybody
+  use callkeys_mod, only: continuum,graybody,callclouds,callmufi, uncoupl_optic_haze
+  use tracer_h, only : nmicro,nice
+  use MMP_OPTICS
+
   implicit none
 
@@ -34,5 +37,6 @@
   ! Input/Output
   !==========================================================
-  REAL*8, INTENT(IN)  :: PQ ! Tracers (kg/kg_of_air).
+  REAL*8, INTENT(IN)  :: PQO(nlay,nmicro) ! Tracers (X/m2).
+  INTEGER, INTENT(IN) :: NLAY             ! Number of pressure layers (for pqo)
   REAL*8, INTENT(IN)  :: PLEV(L_LEVELS), TLEV(L_LEVELS)
   REAL*8, INTENT(IN)  :: TMID(L_LEVELS), PMID(L_LEVELS)
@@ -86,4 +90,10 @@
   integer interm
 
+  real*8 m0as,m3as,m0af,m3af
+  real*8 ext_s,sca_s,ssa_s,asf_s
+  real*8 ext_f,sca_f,ssa_f,asf_f
+  logical,save :: firstcall=.true.
+  !$OMP THREADPRIVATE(firstcall)
+
   !! AS: to save time in computing continuum (see bilinearbig)
   IF (.not.ALLOCATED(indi)) THEN
@@ -127,7 +137,26 @@
         ilay = k / 2 ! int. arithmetic => gives the gcm layer index
         
-        !================= Titan customisation ========================================
-        call disr_haze(dz(k),plev(k),wnoi(nw),dhaze_T(k,nw),SSA_T(k,nw),ASF_T(k,nw))
-        ! =============================================================================
+        ! Optical coupling of YAMMS is plugged but inactivated for now
+        ! as long as the microphysics only isn't fully debugged -- JVO 01/18
+        IF (callmufi .AND. (.NOT. uncoupl_optic_haze)) THEN
+          m0as = pqo(ilay,1)
+          m3as = pqo(ilay,2)
+          m0af = pqo(ilay,3)
+          m3af = pqo(ilay,4)
+
+          IF (.NOT.mmp_sph_optics_ir(m0as,m3as,nw,ext_s,sca_s,ssa_s,asf_s)) &
+          CALL abort_gcm("optcv", "Fatal error in mmp_sph_optics_ir", 12)
+          IF (.NOT.mmp_fra_optics_ir(m0af,m3af,nw,ext_f,sca_f,ssa_f,asf_f)) &
+          CALL abort_gcm("optcv", "Fatal error in mmp_fra_optics_ir", 12)
+          dhaze_T(k,nw) = ext_s+ext_f
+          SSA_T(k,nw)   = (sca_s+sca_f)/dhaze_T(k,nw)
+          ASF_T(k,nw)   = (asf_s*sca_s + asf_f*sca_f) /(sca_s+sca_f)
+          IF (callclouds.and.firstcall) & 
+            WRITE(*,*) 'WARNING: In optci, optical properties &
+                       &calculations are not implemented yet'
+        ELSE
+          ! Call fixed vertical haze profile of extinction - same for all columns
+          call disr_haze(dz(k),plev(k),wnoi(nw),dhaze_T(k,nw),SSA_T(k,nw),ASF_T(k,nw))
+        ENDIF
 
         DCONT = 0.0d0 ! continuum absorption
@@ -401,4 +430,6 @@
 ! ==============================================================================  
 
+  if(firstcall) firstcall = .false.
+
   return
 
Index: trunk/LMDZ.TITAN/libf/phytitan/optcv.F90
===================================================================
--- trunk/LMDZ.TITAN/libf/phytitan/optcv.F90	(revision 1896)
+++ trunk/LMDZ.TITAN/libf/phytitan/optcv.F90	(revision 1897)
@@ -1,3 +1,3 @@
-SUBROUTINE OPTCV(PQ,PLEV,TMID,PMID,  &
+SUBROUTINE OPTCV(PQO,NLAY,PLEV,TMID,PMID,  &
      DTAUV,TAUV,TAUCUMV,WBARV,COSBV,TAURAY,TAUGSURF)
 
@@ -6,5 +6,7 @@
   use gases_h
   use comcstfi_mod, only: g, r
-  use callkeys_mod, only: continuum,graybody,callgasvis
+  use callkeys_mod, only: continuum,graybody,callgasvis,callclouds,callmufi,uncoupl_optic_haze
+  use tracer_h, only: nmicro,nice
+  use MMP_OPTICS
 
   implicit none
@@ -41,5 +43,6 @@
   ! Input/Output
   !==========================================================
-  REAL*8, INTENT(IN)  :: PQ ! Tracers (kg/kg_of_air).
+  REAL*8, INTENT(IN)  :: PQO(nlay,nmicro) ! Tracers (X/m2).
+  INTEGER, INTENT(IN) :: NLAY             ! Number of pressure layers (for pqo)
   REAL*8, INTENT(IN)  :: PLEV(L_LEVELS)
   REAL*8, INTENT(IN)  :: TMID(L_LEVELS), PMID(L_LEVELS)
@@ -97,4 +100,11 @@
   integer interm
 
+  real*8 m0as,m3as,m0af,m3af
+  real*8 ext_s,sca_s,ssa_s,asf_s
+  real*8 ext_f,sca_f,ssa_f,asf_f
+  logical,save :: firstcall=.true.
+  !$OMP THREADPRIVATE(firstcall)
+
+
   !! AS: to save time in computing continuum (see bilinearbig)
   IF (.not.ALLOCATED(indv)) THEN
@@ -149,8 +159,27 @@
 
      do NW=1,L_NSPECTV
-
-        !================= Titan customisation ========================================
-        call disr_haze(dz(k),plev(k),wnov(nw),dhaze_T(k,nw),SSA_T(k,nw),ASF_T(k,nw))
-        ! =============================================================================
+     
+        ! Optical coupling of YAMMS is plugged but inactivated (if false) for now
+        ! as long as the microphysics only isn't fully debugged -- JVO 01/18
+        IF (callmufi .AND. (.NOT. uncoupl_optic_haze)) THEN
+          m0as = pqo(ilay,1)
+          m3as = pqo(ilay,2)
+          m0af = pqo(ilay,3)
+          m3af = pqo(ilay,4)
+
+          IF (.NOT.mmp_sph_optics_vis(m0as,m3as,nw,ext_s,sca_s,ssa_s,asf_s)) &
+          CALL abort_gcm("optcv", "Fatal error in mmp_sph_optics_vis", 12)
+          IF (.NOT.mmp_fra_optics_vis(m0af,m3af,nw,ext_f,sca_f,ssa_f,asf_f)) &
+          CALL abort_gcm("optcv", "Fatal error in mmp_fra_optics_vis", 12)
+          dhaze_T(k,nw) = ext_s+ext_f
+          SSA_T(k,nw)   = (sca_s+sca_f)/dhaze_T(k,nw)
+          ASF_T(k,nw)   = (asf_s*sca_s + asf_f*sca_f) /(sca_s+sca_f)
+          IF (callclouds.and.firstcall) & 
+            WRITE(*,*) 'WARNING: In optcv, optical properties &
+                       &calculations are not implemented yet'
+        ELSE
+          ! Call fixed vertical haze profile of extinction - same for all columns
+          call disr_haze(dz(k),plev(k),wnov(nw),dhaze_T(k,nw),SSA_T(k,nw),ASF_T(k,nw))
+        ENDIF
          
         DRAYAER = TRAY(K,NW)
@@ -355,4 +384,5 @@
 ! ==============================================================================  
 
+  if(firstcall) firstcall = .false.
 
   return
Index: trunk/LMDZ.TITAN/libf/phytitan/physiq_mod.F90
===================================================================
--- trunk/LMDZ.TITAN/libf/phytitan/physiq_mod.F90	(revision 1896)
+++ trunk/LMDZ.TITAN/libf/phytitan/physiq_mod.F90	(revision 1897)
@@ -1,2 +1,4 @@
+#define USE_QTEST
+
       module physiq_mod
       
@@ -46,4 +48,5 @@
       use wxios, only: wxios_context_init, xios_context_finalize
 #endif
+      use MMP_OPTICS
       implicit none
 
@@ -150,4 +153,5 @@
 !                + clean of all too-generic (ocean, water, co2 ...) routines
 !                + Titan's chemistry
+!           Microphysical moment model - J.Burgalat / J.Vatant d'Ollone (2017-2018)
 !============================================================================================
 
@@ -383,8 +387,18 @@
 !$OMP THREADPRIVATE(tankCH4)
 
+      ! -----******----- FOR MUPHYS OPTICS -----******----- 
+      integer :: i,j
+      real :: pqo(ngrid,nlayer,nq)   ! Tracers for the optics (X/m2).
+      real :: i2e(nlayer)            ! int 2 ext factor
+      ! -----******----- END FOR MUPHYS OPTICS -----******----- 
+
+      real,save,dimension(:,:,:), allocatable :: tpq ! Tracers for decoupled microphysical tests ( temporary in 01/18 )
+!$OMP THREADPRIVATE(tpq)
+
+
 !-----------------------------------------------------------------------------
     ! Interface to calmufi 
     !   --> needed in order to pass assumed-shape arrays. Otherwise we must put calmufi in a module 
-    !       (to have an explicit generated by the compiler).
+    !       (to have an explicit interface generated by the compiler).
     !   Or one can put calmufi in MMP_GCM module (in muphytitan).
     INTERFACE
@@ -410,4 +424,6 @@
 ! --------------------------------
       if (firstcall) then
+        allocate(tpq(ngrid,nlayer,nq))
+        tpq(:,:,:) = pq(:,:,:)
 
         ! Initialisation of nmicro as well as tracers names, indexes ...
@@ -518,4 +534,8 @@
 
            call inimufi(nq,ptimestep)
+           
+           ! Optical coupling of YAMMS is plugged but inactivated for now
+           ! as long as the microphysics only isn't fully debugged -- JVO 01/18
+           IF (.NOT.uncoupl_optic_haze) call mmp_initialize_optics("/path/to/mmp_optic_table.nc")
 
          ENDIF
@@ -820,6 +840,18 @@
                call call_profilgases(nlayer)
 
+               ! Convert (microphysical) tracers for optics: X.kg-1 --> X.m-2_
+               ! NOTE: it should be moved somewhere else: calmufi performs the same kind of
+               ! computations... waste of time...
+               DO i = 1, ngrid
+                 i2e(:) = ( pplev(i,1:nlayer)-pplev(i,2:nlayer+1) ) / g 
+                 pqo(i,:,:) = 0.0
+                 DO j=1,nmicro-nice
+                   pqo(i,:,j) = pq(i,:,j)*i2e(:)
+                 ENDDO
+               ENDDO
+
+
                ! standard callcorrk
-               call callcorrk(ngrid,nlayer,pq,nq,qsurf,                           &
+               call callcorrk(ngrid,nlayer,pqo,nq,qsurf,                          &
                               albedo,albedo_equivalent,emis,mu0,pplev,pplay,pt,   &
                               tsurf,fract,dist_star,                              &
@@ -1108,5 +1140,12 @@
  
          if (callmufi) then
-
+#ifdef USE_QTEST
+               if (ngrid.eq.1) then ! We obviously don't have access to (and don't need) zonal means in 1D
+                  call calmufi(pplev,zzlev,pplay,zzlay,pt,tpq,zdqmufi)
+               else
+                  call calmufi(zplevbar,zzlevbar,zplaybar,zzlaybar,ztfibar,tpq,zdqmufi)
+               endif
+               tpq(:,:,:) = tpq(:,:,:) + zdqmufi(1:ngrid,1:nlayer,1:nq)*ptimestep
+#else
             ! Inside this routine we will split 2D->1D, intensive->extensive and separate different types of tracers
             ! Should be put in phytrac
@@ -1119,5 +1158,5 @@
 
             pdq(1:ngrid,1:nlayer,1:nq) = pdq(1:ngrid,1:nlayer,1:nq) + zdqmufi(1:ngrid,1:nlayer,1:nq)   
-
+#endif
          endif ! end of 'callmufi'
       
@@ -1340,66 +1379,4 @@
 
 
-!-----------------------------------
-!        Saving statistics :
-!-----------------------------------
-
-!    Note :("stats" stores and accumulates 8 key variables in file "stats.nc"
-!           which can later be used to make the statistic files of the run:
-!           "stats")          only possible in 3D runs !!!
-
-         
-      if (callstats) then
-
-         call wstats(ngrid,"ps","Surface pressure","Pa",2,ps)
-         call wstats(ngrid,"tsurf","Surface temperature","K",2,tsurf)
-         call wstats(ngrid,"fluxsurf_lw",                               &
-                     "Thermal IR radiative flux to surface","W.m-2",2,  &
-                     fluxsurf_lw)
-         call wstats(ngrid,"fluxtop_lw",                                &
-                     "Thermal IR radiative flux to space","W.m-2",2,    &
-                     fluxtop_lw)
-                     
-!            call wstats(ngrid,"fluxsurf_sw",                               &
-!                        "Solar radiative flux to surface","W.m-2",2,       &
-!                         fluxsurf_sw_tot)                     
-!            call wstats(ngrid,"fluxtop_sw",                                &
-!                        "Solar radiative flux to space","W.m-2",2,         &
-!                        fluxtop_sw_tot)
-
-
-         call wstats(ngrid,"ISR","incoming stellar rad.","W m-2",2,fluxtop_dn)
-         call wstats(ngrid,"ASR","absorbed stellar rad.","W m-2",2,fluxabs_sw)
-         call wstats(ngrid,"OLR","outgoing longwave rad.","W m-2",2,fluxtop_lw)
-         !call wstats(ngrid,"ALB","Surface albedo"," ",2,albedo_equivalent)
-         !call wstats(ngrid,"ALB_1st","First Band Surface albedo"," ",2,albedo(:,1))
-         call wstats(ngrid,"p","Pressure","Pa",3,pplay)
-         call wstats(ngrid,"temp","Atmospheric temperature","K",3,zt)
-         call wstats(ngrid,"u","Zonal (East-West) wind","m.s-1",3,zu)
-         call wstats(ngrid,"v","Meridional (North-South) wind","m.s-1",3,zv)
-         call wstats(ngrid,"w","Vertical (down-up) wind","m.s-1",3,pw)
-         call wstats(ngrid,"q2","Boundary layer eddy kinetic energy","m2.s-2",3,q2)
-
-         if (tracer) then
-            do iq=1,nq
-               call wstats(ngrid,noms(iq),noms(iq),'kg/kg',3,zq(1,1,iq))
-               call wstats(ngrid,trim(noms(iq))//'_surf',trim(noms(iq))//'_surf',  & 
-                           'kg m^-2',2,qsurf(1,iq) )
-                          
-!              call wstats(ngrid,trim(noms(iq))//'_reff',                          & 
-!                          trim(noms(iq))//'_reff',                                   & 
-!                          'm',3,reffrad(1,1,iq))
-
-            end do
-
-         endif ! end of 'tracer' 
-
-         if(lastcall) then
-            write (*,*) "Writing stats..."
-            call mkstats(ierr)
-         endif
-         
-      endif ! end of 'callstats'
-
-
 !-----------------------------------------------------------------------------------------------------
 !           OUTPUT in netcdf file "DIAGFI.NC", containing any variable for diagnostic
@@ -1475,5 +1452,5 @@
         ! Temporary inclusions for winds diagnostics.
         call writediagfi(ngrid,"zdudif","Turbdiff tend. zon. wind","m s-2",3,zdudif)
-	call writediagfi(ngrid,"zdudyn","Dyn. tend. zon. wind","m s-2",3,zdudyn)
+        call writediagfi(ngrid,"zdudyn","Dyn. tend. zon. wind","m s-2",3,zdudyn)
 
         ! Temporary inclusions for heating diagnostics.
@@ -1491,8 +1468,21 @@
 
          if (callmufi) then ! For now we assume an given order for tracers !
+#ifdef USE_QTEST
+            ! Microphysical tracers passed through dyn+phys(except mufi)
+            call writediagfi(ngrid,"mu_m0as_dp","Dynphys only spherical mode 0th order moment",'kg/kg',3,zq(:,:,1))
+            call writediagfi(ngrid,"mu_m3as_dp","Dynphys only spherical mode 3rd order moment",'kg/kg',3,zq(:,:,2))
+            call writediagfi(ngrid,"mu_m0af_dp","Dynphys only fractal mode 0th order moment",'kg/kg',3,zq(:,:,3))
+            call writediagfi(ngrid,"mu_m3af_dp","Dynphys only fractal mode 3rd order moment",'kg/kg',3,zq(:,:,4))
+            ! Microphysical tracers passed through mufi only
+            call writediagfi(ngrid,"mu_m0as_mo","Mufi only spherical mode 0th order moment",'kg/kg',3,tpq(:,:,1))
+            call writediagfi(ngrid,"mu_m3as_mo","Mufi only spherical mode 3rd order moment",'kg/kg',3,tpq(:,:,2))
+            call writediagfi(ngrid,"mu_m0af_mo","Mufi only fractal mode 0th order moment",'kg/kg',3,tpq(:,:,3))
+            call writediagfi(ngrid,"mu_m3af_mo","Mufi only fractal mode 3rd order moment",'kg/kg',3,tpq(:,:,4))
+#else
             call writediagfi(ngrid,"mu_m0as","Spherical mode 0th order moment",'kg/kg',3,zq(:,:,1))
             call writediagfi(ngrid,"mu_m3as","Spherical mode 3rd order moment",'kg/kg',3,zq(:,:,2))
             call writediagfi(ngrid,"mu_m0af","Fractal mode 0th order moment",'kg/kg',3,zq(:,:,3))
             call writediagfi(ngrid,"mu_m3af","Fractal mode 3rd order moment",'kg/kg',3,zq(:,:,4))
+#endif
          endif ! end of 'callmufi'
 
Index: trunk/LMDZ.TITAN/libf/phytitan/setspi.F90
===================================================================
--- trunk/LMDZ.TITAN/libf/phytitan/setspi.F90	(revision 1896)
+++ trunk/LMDZ.TITAN/libf/phytitan/setspi.F90	(revision 1897)
@@ -138,4 +138,8 @@
       end do
 !     note M=L_NSPECTI+1 after loop due to Fortran bizarreness
+!        --> No fortran bizarreness here... incrementation is performed at the end of the loop...
+!        --> then when M reached L_NSPECTI, we initialiaze the last element of each array and
+!            ... increment one last time M... tadaaaa, mystery solved !
+!            The same logic is applied on for loop in C !
 
 !=======================================================================
