Index: LMDZ6/trunk/libf/dyn3d/dynetat0.f90
===================================================================
--- LMDZ6/trunk/libf/dyn3d/dynetat0.f90	(revision 4045)
+++ LMDZ6/trunk/libf/dyn3d/dynetat0.f90	(revision 4046)
@@ -10,4 +10,5 @@
                          NF90_CLOSE, NF90_GET_VAR
   USE control_mod, ONLY: planet_type
+  USE strings_mod, ONLY: maxlen
   USE assert_eq_m, ONLY: assert_eq
   USE comvert_mod, ONLY: pa,preff
@@ -36,5 +37,5 @@
 !===============================================================================
 ! Local variables:
-  CHARACTER(LEN=256) :: msg, var, modname
+  CHARACTER(LEN=maxlen) :: msg, var, modname
   INTEGER, PARAMETER :: length=100
   INTEGER :: iq, fID, vID, idecal!, iml, jml, lml, nqt
@@ -126,5 +127,5 @@
 !--- Tracers
   DO iq=1,nqtot
-    var=tname(iq)
+    var=TRIM(tracers(iq)%name)
     IF(NF90_INQ_VARID(fID,var,vID)==NF90_NoErr) THEN
       CALL err(NF90_GET_VAR(fID,vID,q(:,:,:,iq)),"get",var); CYCLE
@@ -153,5 +154,5 @@
   INTEGER,          INTENT(IN) :: n1, n2
   CHARACTER(LEN=*), INTENT(IN) :: str1, str2
-  CHARACTER(LEN=100) :: s1, s2
+  CHARACTER(LEN=maxlen) :: s1, s2
   IF(n1/=n2) THEN
     s1='value of '//TRIM(str1)//' ='
Index: LMDZ6/trunk/libf/dyn3d/dynredem.F90
===================================================================
--- LMDZ6/trunk/libf/dyn3d/dynredem.F90	(revision 4045)
+++ LMDZ6/trunk/libf/dyn3d/dynredem.F90	(revision 4046)
@@ -12,4 +12,5 @@
                     NF90_64BIT_OFFSET
   USE dynredem_mod, ONLY: cre_var, put_var1, put_var2, err, modname, fil
+  USE strings_mod, ONLY: maxlen
   USE comvert_mod, ONLY: ap,bp,aps,bps,presnivs,pseudoalt,pa,preff, &
                               nivsig,nivsigs
@@ -38,5 +39,5 @@
   REAL    :: tab_cntrl(length)                     !--- RUN PARAMETERS TABLE
 !   For NetCDF:
-  CHARACTER(LEN=30) :: unites
+  CHARACTER(LEN=maxlen) :: unites
   INTEGER :: indexID
   INTEGER :: rlonuID, rlonvID, rlatuID, rlatvID
@@ -145,5 +146,5 @@
   CALL cre_var(nid,"teta" ,"Temperature",[rlonvID,rlatuID,sID,timID])
   DO iq=1,nqtot
-    CALL cre_var(nid,tname(iq),ttext(iq),[rlonvID,rlatuID,sID,timID])
+    CALL cre_var(nid,tracers(iq)%name,tracers(iq)%longName,[rlonvID,rlatuID,sID,timID])
   END DO
   CALL cre_var(nid,"masse","Masse d air"    ,[rlonvID,rlatuID,sID,timID])
@@ -196,5 +197,5 @@
   INTEGER, PARAMETER :: length=100
   REAL               :: tab_cntrl(length) ! tableau des parametres du run
-  CHARACTER(LEN=256) :: var, dum
+  CHARACTER(LEN=maxlen) :: var, dum
   LOGICAL            :: lread_inca
 !===============================================================================
@@ -231,5 +232,5 @@
 
 !--- Save tracers
-  DO iq=1,nqtot; var=tname(iq); ierr=-1
+  DO iq=1,nqtot; var=TRIM(tracers(iq)%name); ierr=-1
     IF(lread_inca) THEN                  !--- Possibly read from "start_trac.nc"
       fil="start_trac.nc"
Index: LMDZ6/trunk/libf/dyn3d_common/infotrac.F90
===================================================================
--- LMDZ6/trunk/libf/dyn3d_common/infotrac.F90	(revision 4045)
+++ LMDZ6/trunk/libf/dyn3d_common/infotrac.F90	(revision 4046)
@@ -2,4 +2,6 @@
 !
 MODULE infotrac
+
+  USE readTracFiles_mod, ONLY: trac_type, isot_type, maxlen, ancestor, delPhase
 
 ! nqtot : total number of tracers and higher order of moment, water vapor and liquid included
@@ -26,8 +28,7 @@
   INTEGER, SAVE :: nqCO2
 
-! Name variables
-  INTEGER,PARAMETER :: tname_lenmax=128 
-  CHARACTER(len=tname_lenmax), ALLOCATABLE, DIMENSION(:), SAVE :: tname ! tracer short name for restart and diagnostics
-  CHARACTER(len=tname_lenmax+3), ALLOCATABLE, DIMENSION(:), SAVE :: ttext ! tracer long name for diagnostics
+! DC: derived types containing informations about tracers and isotopes
+  TYPE(trac_type), TARGET,  SAVE, ALLOCATABLE ::  tracers(:)    !=== TRACERS DESCRIPTORS VECTOR
+  TYPE(isot_type), TARGET,  SAVE, ALLOCATABLE :: isotopes(:)    !=== ISOTOPES PARAMETERS VECTOR
 
 ! iadv  : index of trasport schema for each tracer
@@ -119,10 +120,10 @@
     CHARACTER(len=8), ALLOCATABLE, DIMENSION(:) :: solsym_inca
 
-    CHARACTER(len=tname_lenmax), ALLOCATABLE, DIMENSION(:) :: tnom_0  ! tracer short name
-    CHARACTER(len=tname_lenmax), ALLOCATABLE, DIMENSION(:) :: tnom_transp ! transporting fluid short name: CRisi
+    CHARACTER(len=maxlen), ALLOCATABLE, DIMENSION(:) :: tnom_0  ! tracer short name
+    CHARACTER(len=maxlen), ALLOCATABLE, DIMENSION(:) :: tnom_transp ! transporting fluid short name: CRisi
     CHARACTER(len=3), DIMENSION(30) :: descrq
     CHARACTER(len=1), DIMENSION(3)  :: txts
     CHARACTER(len=2), DIMENSION(9)  :: txtp
-    CHARACTER(len=tname_lenmax)               :: str1,str2
+    CHARACTER(len=maxlen)           :: str1,str2
   
     INTEGER :: nqtrue  ! number of tracers read from tracer.def, without higer order of moment
@@ -131,5 +132,5 @@
     LOGICAL :: continu,nouveau_traceurdef
     INTEGER :: IOstatus ! gestion de la retrocompatibilite de traceur.def
-    CHARACTER(len=2*tname_lenmax+1) :: tchaine    
+    CHARACTER(len=maxlen) :: tchaine    
 
     character(len=*),parameter :: modname="infotrac_init"
@@ -349,6 +350,6 @@
              if (nouveau_traceurdef) then
                 write(lunout,*) 'C''est la nouvelle version de traceur.def'
-                tnom_0(iq)=tchaine(1:iiq-1)
-                tnom_transp(iq)=tchaine(iiq+1:)
+                tnom_0(iq)=TRIM(tchaine(1:iiq-1))
+                tnom_transp(iq)=TRIM(tchaine(iiq+1:))
              else
                 write(lunout,*) 'C''est l''ancienne version de traceur.def'
@@ -563,5 +564,5 @@
 ! Allocate variables with total number of tracers, nqtot
 !
-    ALLOCATE(tname(nqtot), ttext(nqtot))
+    ALLOCATE(tracers(nqtot))
     ALLOCATE(iadv(nqtot), niadv(nqtot))
 
@@ -587,19 +588,19 @@
       
        str1=tnom_0(iq)
-       tname(new_iq)= tnom_0(iq)
+       tracers(new_iq)%name=TRIM(tnom_0(iq))
        IF (iadv(new_iq)==0) THEN
-          ttext(new_iq)=trim(str1)
+          tracers(new_iq)%longName=trim(str1)
        ELSE
-          ttext(new_iq)=trim(tnom_0(iq))//descrq(iadv(new_iq))
+          tracers(new_iq)%longName=trim(tnom_0(iq))//descrq(iadv(new_iq))
        ENDIF
 
        ! schemas tenant compte des moments d'ordre superieur
-       str2=ttext(new_iq)
+       str2=TRIM(tracers(new_iq)%longName)
        IF (iadv(new_iq)==20) THEN
           DO jq=1,3
              new_iq=new_iq+1
              iadv(new_iq)=-20
-             ttext(new_iq)=trim(str2)//txts(jq)
-             tname(new_iq)=trim(str1)//txts(jq)
+             tracers(new_iq)%longName=trim(str2)//txts(jq)
+             tracers(new_iq)%name=trim(str1)//txts(jq)
           END DO
        ELSE IF (iadv(new_iq)==30) THEN
@@ -607,6 +608,6 @@
              new_iq=new_iq+1
              iadv(new_iq)=-30
-             ttext(new_iq)=trim(str2)//txtp(jq)
-             tname(new_iq)=trim(str1)//txtp(jq)
+             tracers(new_iq)%longName=trim(str2)//txtp(jq)
+             tracers(new_iq)%name=trim(str1)//txtp(jq)
           END DO
        ENDIF
@@ -628,8 +629,8 @@
 
     WRITE(lunout,*) trim(modname),': Information stored in infotrac :'
-    WRITE(lunout,*) trim(modname),': iadv  niadv tname  ttext :'
+    WRITE(lunout,*) trim(modname),': iadv  niadv  name  long_name :'
 
     DO iq=1,nqtot
-       WRITE(lunout,*) iadv(iq),niadv(iq), ' ',trim(tname(iq)),' ',trim(ttext(iq))
+       WRITE(lunout,*) iadv(iq),niadv(iq), ' ',trim(tracers(iq)%name),' ',trim(tracers(iq)%longName)
     END DO
 
@@ -802,5 +803,5 @@
     INTEGER, ALLOCATABLE,DIMENSION(:) :: nb_isoind
     INTEGER :: ntraceurs_zone_prec,iq,phase,ixt,iiso,izone
-    CHARACTER(len=tname_lenmax) :: tnom_trac
+    CHARACTER(len=maxlen) :: tnom_trac
     INCLUDE "iniprint.h"
 
Index: LMDZ6/trunk/libf/dyn3d_common/initdynav.F90
===================================================================
--- LMDZ6/trunk/libf/dyn3d_common/initdynav.F90	(revision 4045)
+++ LMDZ6/trunk/libf/dyn3d_common/initdynav.F90	(revision 4046)
@@ -6,5 +6,5 @@
   USE IOIPSL
 #endif
-  USE infotrac, ONLY : nqtot, ttext
+  USE infotrac, ONLY : nqtot
   use com_io_dyn_mod, only : histaveid,histvaveid,histuaveid, &
        dynhistave_file,dynhistvave_file,dynhistuave_file
@@ -158,5 +158,6 @@
 
   !        DO iq=1,nqtot
-  !          call histdef(histaveid, ttext(iq), ttext(iq), '-', &
+  !          call histdef(histaveid, tracers(iq)%name, &
+  !                                  tracers(iq)%longName, '-',  &
   !                  iip1, jjp1, thoriid, llm, 1, llm, zvertiid, &
   !                  32, 'ave(X)', t_ops, t_wrt)
Index: LMDZ6/trunk/libf/dyn3d_common/inithist.F
===================================================================
--- LMDZ6/trunk/libf/dyn3d_common/inithist.F	(revision 4045)
+++ LMDZ6/trunk/libf/dyn3d_common/inithist.F	(revision 4046)
@@ -7,5 +7,5 @@
        USE IOIPSL
 #endif
-       USE infotrac, ONLY : nqtot, ttext
+       USE infotrac, ONLY : nqtot
        use com_io_dyn_mod, only : histid,histvid,histuid,               &
      &                        dynhist_file,dynhistv_file,dynhistu_file
@@ -157,5 +157,6 @@
 !
 !        DO iq=1,nqtot
-!          call histdef(histid, ttext(iq),  ttext(iq), '-',
+!          call histdef(histid, tracers(iq)%name, 
+!                               tracers(iq)%longName, '-',
 !     .             iip1, jjp1, thoriid, llm, 1, llm, zvertiid,
 !     .             32, 'inst(X)', t_ops, t_wrt)
Index: LMDZ6/trunk/libf/dyn3d_common/writedynav.F90
===================================================================
--- LMDZ6/trunk/libf/dyn3d_common/writedynav.F90	(revision 4045)
+++ LMDZ6/trunk/libf/dyn3d_common/writedynav.F90	(revision 4046)
@@ -6,5 +6,5 @@
   USE ioipsl
 #endif
-  USE infotrac, ONLY : nqtot, ttext
+  USE infotrac, ONLY : nqtot
   use com_io_dyn_mod, only : histaveid, histvaveid, histuaveid
   USE comconst_mod, ONLY: cpp
@@ -106,6 +106,6 @@
 
   !  DO iq=1, nqtot
-  !       call histwrite(histaveid, ttext(iq), itau_w, q(:, :, iq), &
-  !                   iip1*jjp1*llm, ndexu)
+  !       call histwrite(histaveid, tracers(iq)%longName, itau_w, &
+  !                   q(:, :, iq), iip1*jjp1*llm, ndexu)
   ! enddo
 
Index: LMDZ6/trunk/libf/dyn3d_common/writehist.F
===================================================================
--- LMDZ6/trunk/libf/dyn3d_common/writehist.F	(revision 4045)
+++ LMDZ6/trunk/libf/dyn3d_common/writehist.F	(revision 4046)
@@ -7,5 +7,5 @@
       USE ioipsl
 #endif
-      USE infotrac, ONLY : nqtot, ttext
+      USE infotrac, ONLY : nqtot
       use com_io_dyn_mod, only : histid,histvid,histuid
       USE temps_mod, ONLY: itau_dyn
@@ -100,6 +100,6 @@
 C
 !        DO iq=1,nqtot
-!          call histwrite(histid, ttext(iq), itau_w, q(:,:,iq), 
-!     .                   iip1*jjp1*llm, ndexu)
+!          call histwrite(histid, tracers(iq)%longName, itau_w, 
+!     .                   q(:,:,iq), iip1*jjp1*llm, ndexu)
 !        enddo
 !C
Index: LMDZ6/trunk/libf/dyn3dmem/dynetat0_loc.F90
===================================================================
--- LMDZ6/trunk/libf/dyn3dmem/dynetat0_loc.F90	(revision 4045)
+++ LMDZ6/trunk/libf/dyn3dmem/dynetat0_loc.F90	(revision 4046)
@@ -11,4 +11,5 @@
       NF90_NOWRITE, NF90_CLOSE, NF90_INQUIRE_VARIABLE,  NF90_GET_VAR, NF90_NoErr
   USE control_mod, ONLY: planet_type
+  USE strings_mod, ONLY: maxlen
   USE assert_eq_m, ONLY: assert_eq
   USE comvert_mod, ONLY: pa,preff
@@ -39,5 +40,5 @@
 !===============================================================================
 ! Local variables:
-  CHARACTER(LEN=256) :: msg, var, modname
+  CHARACTER(LEN=maxlen) :: msg, var, modname
   INTEGER, PARAMETER :: length=100
   INTEGER :: iq, fID, vID, idecal, ierr
@@ -152,5 +153,5 @@
   ALLOCATE(q_glo(ip1jmp1,llm))
   DO iq=1,nqtot
-    var=tname(iq)
+    var=TRIM(tracers(iq)%name)
 #ifdef INCA
     IF (var .eq. "O3" ) THEN 
@@ -191,5 +192,5 @@
   INTEGER,          INTENT(IN) :: n1, n2
   CHARACTER(LEN=*), INTENT(IN) :: str1, str2
-  CHARACTER(LEN=256) :: s1, s2
+  CHARACTER(LEN=maxlen) :: s1, s2
   IF(n1/=n2) THEN
     s1='value of '//TRIM(str1)//' ='
Index: LMDZ6/trunk/libf/dyn3dmem/dynredem_loc.F90
===================================================================
--- LMDZ6/trunk/libf/dyn3dmem/dynredem_loc.F90	(revision 4045)
+++ LMDZ6/trunk/libf/dyn3dmem/dynredem_loc.F90	(revision 4046)
@@ -14,4 +14,5 @@
                     NF90_64BIT_OFFSET
   USE dynredem_mod, ONLY: cre_var, put_var, err, modname, fil
+  USE strings_mod, ONLY: maxlen
   USE comvert_mod, ONLY: ap,bp,aps,bps,presnivs,pseudoalt,pa,preff, &
                          nivsig,nivsigs
@@ -41,5 +42,5 @@
   REAL    :: phis_glo(ip1jmp1)
 !   For NetCDF:
-  CHARACTER(LEN=30) :: unites
+  CHARACTER(LEN=maxlen) :: unites
   INTEGER :: indexID
   INTEGER :: rlonuID, rlonvID, rlatuID, rlatvID
@@ -151,5 +152,5 @@
   CALL cre_var(nid,"teta" ,"Temperature",[rlonvID,rlatuID,sID,timID])
   DO iq=1,nqtot
-    CALL cre_var(nid,tname(iq),ttext(iq),[rlonvID,rlatuID,sID,timID])
+    CALL cre_var(nid,tracers(iq)%name,tracers(iq)%longName,[rlonvID,rlatuID,sID,timID])
   END DO
   CALL cre_var(nid,"masse","Masse d air"    ,[rlonvID,rlatuID,sID,timID])
@@ -204,5 +205,5 @@
   INTEGER, PARAMETER :: length=100
   REAL               :: tab_cntrl(length) ! tableau des parametres du run
-  CHARACTER(LEN=256) :: var, dum
+  CHARACTER(LEN=maxlen) :: var, dum
   LOGICAL            :: lread_inca
 !===============================================================================
@@ -248,5 +249,5 @@
 
 !--- Save tracers
-  DO iq=1,nqtot; var=tname(iq); ierr=-1
+  DO iq=1,nqtot; var=TRIM(tracers(iq)%name); ierr=-1
     IF(lread_inca) THEN                  !--- Possibly read from "start_trac.nc"
 !$OMP MASTER      
Index: LMDZ6/trunk/libf/dyn3dmem/initdynav_loc.F
===================================================================
--- LMDZ6/trunk/libf/dyn3dmem/initdynav_loc.F	(revision 4045)
+++ LMDZ6/trunk/libf/dyn3dmem/initdynav_loc.F	(revision 4046)
@@ -252,5 +252,6 @@
 C
 !        DO iq=1,nqtot
-!          call histdef(histaveid, ttext(iq), ttext(iq), '-',
+!          call histdef(histaveid, tracers(iq)%name,
+!     .                            tracers(iq)%longName, '-',
 !     .             iip1, jjn, thoriid, llm, 1, llm, zvertiid,
 !     .             32, 'ave(X)', t_ops, t_wrt)
Index: LMDZ6/trunk/libf/dyn3dmem/inithist_loc.F
===================================================================
--- LMDZ6/trunk/libf/dyn3dmem/inithist_loc.F	(revision 4045)
+++ LMDZ6/trunk/libf/dyn3dmem/inithist_loc.F	(revision 4046)
@@ -253,5 +253,6 @@
 C
 !        DO iq=1,nqtot
-!          call histdef(histid, ttext(iq), ttext(iq), '-',
+!          call histdef(histid, tracers(iq)%name, 
+!     .             tracers(iq)%longName, '-', 
 !     .             iip1, jjn, thoriid, llm, 1, llm, zvertiid,
 !     .             32, 'inst(X)', t_ops, t_wrt)
Index: LMDZ6/trunk/libf/dyn3dmem/writedynav_loc.F
===================================================================
--- LMDZ6/trunk/libf/dyn3dmem/writedynav_loc.F	(revision 4045)
+++ LMDZ6/trunk/libf/dyn3dmem/writedynav_loc.F	(revision 4046)
@@ -11,5 +11,5 @@
       USE parallel_lmdz
       USE misc_mod
-      USE infotrac, ONLY : nqtot, ttext
+      USE infotrac, ONLY : nqtot
       use com_io_dyn_mod, only : histaveid,histvaveid,histuaveid
       USE comconst_mod, ONLY: cpp
@@ -177,6 +177,6 @@
 !!$OMP MASTER      
 !        DO iq=1,nqtot
-!          call histwrite(histaveid, ttext(iq), itau_w, q(ijb:ije,:,iq), 
-!     .                   iip1*jjn*llm, ndexu)
+!          call histwrite(histaveid, tracers(iq)%longName, itau_w, &
+!     .                   q(ijb:ije,:,iq), iip1*jjn*llm, ndexu)
 !        enddo
 !!$OMP END MASTER
Index: LMDZ6/trunk/libf/dyn3dmem/writehist_loc.F
===================================================================
--- LMDZ6/trunk/libf/dyn3dmem/writehist_loc.F	(revision 4045)
+++ LMDZ6/trunk/libf/dyn3dmem/writehist_loc.F	(revision 4046)
@@ -11,5 +11,5 @@
       USE parallel_lmdz
       USE misc_mod
-      USE infotrac, ONLY : nqtot, ttext
+      USE infotrac, ONLY : nqtot
       use com_io_dyn_mod, only : histid,histvid,histuid
       USE comconst_mod, ONLY: cpp
@@ -177,6 +177,6 @@
 !!$OMP MASTER      
 !        DO iq=1,nqtot
-!          call histwrite(histid, ttext(iq), itau_w, q(ijb:ije,:,iq), 
-!     .                   iip1*jjn*llm, ndexu)
+!          call histwrite(histid, tracers(iq)%longName, itau_w, 
+!     .                   q(ijb:ije,:,iq), iip1*jjn*llm, ndexu)
 !        enddo
 !!$OMP END MASTER
Index: LMDZ6/trunk/libf/dynphy_lonlat/calfis.F
===================================================================
--- LMDZ6/trunk/libf/dynphy_lonlat/calfis.F	(revision 4045)
+++ LMDZ6/trunk/libf/dynphy_lonlat/calfis.F	(revision 4046)
@@ -29,5 +29,5 @@
 c    Auteur :  P. Le Van, F. Hourdin 
 c   .........
-      USE infotrac, ONLY: nqtot, niadv, tname
+      USE infotrac, ONLY: nqtot, niadv, tracers
       USE control_mod, ONLY: planet_type, nsplit_phys
 #ifdef CPP_PHYS
@@ -481,5 +481,5 @@
          lafin_split=lafin.and.isplit==nsplit_phys
 
-        CALL call_physiq(ngridmx,llm,nqtot,tname,
+        CALL call_physiq(ngridmx,llm,nqtot,tracers(:)%name,
      &                   debut_split,lafin_split,
      &                   jD_cur,jH_cur_split,zdt_split,
@@ -522,5 +522,5 @@
 !     .             llm,            !! nlayer
 !     .             nqtot,          !! nq
-!     .             tname,          !! tracer names from dynamical core (given in infotrac)
+!     .             tracers(:)%name,!! tracer names from dynamical core (given in infotrac)
 !     .             debut_split,    !! firstcall 
 !     .             lafin_split,    !! lastcall
Index: LMDZ6/trunk/libf/dynphy_lonlat/calfis_loc.F
===================================================================
--- LMDZ6/trunk/libf/dynphy_lonlat/calfis_loc.F	(revision 4045)
+++ LMDZ6/trunk/libf/dynphy_lonlat/calfis_loc.F	(revision 4046)
@@ -45,5 +45,5 @@
       USE Times
 #endif
-      USE infotrac, ONLY: nqtot, niadv, tname
+      USE infotrac, ONLY: nqtot, niadv, tracers
       USE control_mod, ONLY: planet_type, nsplit_phys
 #ifdef CPP_PHYS
@@ -731,5 +731,5 @@
          lafin_split=lafin.and.isplit==nsplit_phys
 
-        CALL call_physiq(klon,llm,nqtot,tname,
+        CALL call_physiq(klon,llm,nqtot,tracers(:)%name,
      &                   debut_split,lafin_split,
      &                   jD_cur,jH_cur_split,zdt_split,
Index: LMDZ6/trunk/libf/dynphy_lonlat/phylmd/etat0dyn_netcdf.F90
===================================================================
--- LMDZ6/trunk/libf/dynphy_lonlat/phylmd/etat0dyn_netcdf.F90	(revision 4045)
+++ LMDZ6/trunk/libf/dynphy_lonlat/phylmd/etat0dyn_netcdf.F90	(revision 4046)
@@ -39,4 +39,5 @@
   USE comvert_mod, ONLY: ap, bp, preff, pressure_exner
   USE temps_mod, ONLY: annee_ref, day_ref, itau_dyn, itau_phy, start_time
+  USE strings_mod, ONLY: strLower
   
   IMPLICIT NONE
@@ -74,5 +75,5 @@
   USE exner_hyb_m,    ONLY: exner_hyb
   USE exner_milieu_m, ONLY: exner_milieu
-  USE infotrac,       ONLY: nqtot, tname
+  USE infotrac,       ONLY: nqtot, tracers
   USE filtreg_mod
   IMPLICIT NONE
@@ -84,5 +85,5 @@
 ! Local variables:
   CHARACTER(LEN=256) :: modname, fmt
-  INTEGER            :: i, j, l, ji, itau, iday
+  INTEGER            :: i, j, l, ji, itau, iday, iq
   REAL               :: xpn, xps, time, phystep
   REAL, DIMENSION(iip1,jjp1)       :: psol
@@ -145,5 +146,5 @@
 ! Look for ozone tracer:
 #ifndef INCA
-  DO i=1,nqtot; IF(ANY(["O3","o3"]==tname(i))) EXIT; END DO
+  DO iq=1,nqtot; IF(strLower(tracers(iq)%name)=="o3") EXIT; END DO
   IF(i/=nqtot+1) THEN
     CALL regr_lat_time_coefoz
Index: LMDZ6/trunk/libf/dynphy_lonlat/phylmd/iniphysiq_mod.F90
===================================================================
--- LMDZ6/trunk/libf/dynphy_lonlat/phylmd/iniphysiq_mod.F90	(revision 4045)
+++ LMDZ6/trunk/libf/dynphy_lonlat/phylmd/iniphysiq_mod.F90	(revision 4046)
@@ -16,5 +16,5 @@
   USE mod_phys_lmdz_para, ONLY: klon_omp ! number of columns (on local omp grid)
   USE vertical_layers_mod, ONLY : init_vertical_layers
-  USE infotrac, ONLY: nqtot,nqo,nbtr,nqCO2,tname,ttext,type_trac,&
+  USE infotrac, ONLY: nqtot,nqo,nbtr,nqCO2,tracers,type_trac,&
                       niadv,conv_flg,pbl_flg,solsym,&
                       nqfils,nqdesc,nqdesc_tot,iqfils,iqpere,&
@@ -146,5 +146,5 @@
 
   ! Initialize tracer names, numbers, etc. for physics
-  CALL init_infotrac_phy(nqtot,nqo,nbtr,nqtottr,nqCO2,tname,ttext,type_trac,&
+  CALL init_infotrac_phy(nqtot,nqo,nbtr,nqtottr,nqCO2,tracers,type_trac,&
                          niadv,conv_flg,pbl_flg,solsym,&
                          nqfils,nqdesc,nqdesc_tot,iqfils,iqpere,&
@@ -203,9 +203,9 @@
           rlonudyn,rlatudyn,rlonvdyn,rlatvdyn)
 #endif
-    IF (type_trac == 'repr') THEN
-#ifdef REPROBUS
-       CALL Init_chem_rep_phys(klon_omp,nbp_lev)
-#endif
-    END IF
+  END IF
+  IF (type_trac == 'repr') THEN
+#ifdef REPROBUS
+     CALL Init_chem_rep_phys(klon_omp,nbp_lev)
+#endif
   END IF
 
Index: LMDZ6/trunk/libf/misc/readTracFiles_mod.f90
===================================================================
--- LMDZ6/trunk/libf/misc/readTracFiles_mod.f90	(revision 4046)
+++ LMDZ6/trunk/libf/misc/readTracFiles_mod.f90	(revision 4046)
@@ -0,0 +1,1334 @@
+MODULE readTracFiles_mod
+
+  USE strings_mod, ONLY:   msg, testFile,  strFind, strStack, strReduce,  strHead, strCount,   find, maxlen, fmsg, &
+            removeComment, cat, checkList, strIdx,  strParse, strReplace, strTail, reduceExpr, test, get_in, dispTable
+  USE trac_types_mod, ONLY : trac_type, isot_type, keys_type
+
+  IMPLICIT NONE
+
+  PRIVATE
+
+  PUBLIC :: initIsotopes, maxlen, trac_type, isot_type
+  PUBLIC :: readTracersFiles, aliasTracer, tracersSubset, indexUpdate!--- TOOLS ASSOCIATED TO TRACERS  DESCRIPTORS
+  PUBLIC :: readIsotopesFile                                         !--- TOOLS ASSOCIATED TO ISOTOPES DESCRIPTORS
+  PUBLIC :: getKey_init, getKey, setDirectKeys                       !--- GET/SET KEYS FROM/TO tracers & isotopes
+
+  PUBLIC :: known_phases, old_phases, nphases, phases_names, &       !--- VARIABLES RELATED TO THE PHASES
+            phases_sep, delPhase, addPhase                           !--- + ROUTINES TO ADD/REMOVE PHASE TO/FROM A NAME
+
+  PUBLIC :: tran0, idxAncestor, ancestor                             !--- GENERATION 0 TRACER + TOOLS FOR GENERATIONS
+
+!------------------------------------------------------------------------------------------------------------------------------
+  TYPE :: dataBase_type                                              !=== TYPE FOR TRACERS SECTION
+    CHARACTER(LEN=maxlen)  :: name                                   !--- Section name
+    TYPE(trac_type), ALLOCATABLE :: trac(:)                          !--- Tracers descriptors
+  END TYPE dataBase_type
+!------------------------------------------------------------------------------------------------------------------------------
+  INTERFACE getKey
+    MODULE PROCEDURE getKeyByName_s1, getKeyByName_i1, getKeyByName_r1, getKeyByName_sm, getKeyByName_im, getKeyByName_rm
+  END INTERFACE getKey
+!------------------------------------------------------------------------------------------------------------------------------
+  INTERFACE tracersSubset; MODULE PROCEDURE trSubset_Indx, trSubset_Name, trSubset_gen0Name; END INTERFACE tracersSubset
+  INTERFACE idxAncestor;   MODULE PROCEDURE idxAncestor_1, idxAncestor_m; END INTERFACE idxAncestor
+  INTERFACE    ancestor;   MODULE PROCEDURE    ancestor_1,    ancestor_m; END INTERFACE    ancestor
+  INTERFACE    addPhase;   MODULE PROCEDURE    addPhase_1,    addPhase_m; END INTERFACE    addPhase
+!------------------------------------------------------------------------------------------------------------------------------
+
+  !=== MAIN DATABASE: files sections descriptors
+  TYPE(dataBase_type), SAVE, ALLOCATABLE, TARGET :: dBase(:)
+
+  !--- SOME PARAMETERS THAT ARE NOT LIKELY TO CHANGE OFTEN
+  CHARACTER(LEN=maxlen), SAVE      :: tran0        = 'air'           !--- Default transporting fluid
+  CHARACTER(LEN=maxlen), PARAMETER :: old_phases   = 'vli'           !--- Old phases for water (no separator)
+  CHARACTER(LEN=maxlen), PARAMETER :: known_phases = 'gls'           !--- Known phases initials
+  INTEGER,               PARAMETER :: nphases=LEN_TRIM(known_phases) !--- Number of phases
+  CHARACTER(LEN=maxlen), SAVE      :: phases_names(nphases) &        !--- Known phases names
+                                = ['gaseous', 'liquid ', 'solid  ']
+  CHARACTER(LEN=1),   SAVE      :: phases_sep  =  '_'                !--- Phase separator
+  LOGICAL,            SAVE      :: tracs_merge = .TRUE.              !--- Merge/stack tracers lists
+  LOGICAL,            SAVE      :: lSortByGen  = .TRUE.              !--- Sort by growing generation
+
+  !=== LOCAL COPIES OF THE TRACERS AND ISOTOPES DESCRIPTORS, USED BY getKey (INITIALIZED IN getKey_init)
+  TYPE(trac_type), ALLOCATABLE, TARGET, SAVE ::  tracers(:)
+  TYPE(isot_type), ALLOCATABLE, TARGET, SAVE :: isotopes(:)
+
+  CHARACTER(LEN=maxlen) :: modname
+
+CONTAINS
+
+!==============================================================================================================================
+!==============================================================================================================================
+!=== READ ONE OR SEVERAL TRACER FILES AND FILL A "tr" TRACERS DESCRIPTOR DERIVED TYPE.
+!=== THE RETURN VALUE fType DEPENDS ON WHAT IS FOUND:
+!===  0: NO ADEQUATE FILE FOUND ; DEFAULT VALUES MUST BE USED
+!===  1: AN "OLD STYLE" TRACERS FILE "traceur.def":
+!===    First line: <nb tracers>     Other lines: <hadv> <vadv> <tracer name> [<parent name>]
+!===  2: A  "NEW STYLE" TRACERS FILE  "tracer.def" WITH SEVERAL SECTIONS.
+!===  3: SEVERAL  "  "  TRACERS FILES "tracer_<component>.def" WITH A SINGLE SECTION IN EACH.
+!=== REMARKS:
+!===  * EACH SECTION BEGINS WITH A "&<section name> LINE
+!===  * DEFAULT VALUES FOR ALL THE SECTIONS OF THE FILE ARE DEFINED IN THE SPECIAL SECTION "&default"
+!===  * EACH SECTION LINE HAS THE STRUCTURE:  <name(s)>  <key1>=<value1> <key2>=<value2> ...
+!===  * SO FAR, THE DEFINED KEYS ARE: parent, phases, hadv, vadv, type
+!===  * <name> AND <parent> CAN BE LISTS OF COMA-SEPARATED TRACERS ; THE ROUTINE EXPAND THESE FACTORIZATIONS.
+!=== FUNCTION RETURN VALUE "lerr" IS FALSE IN CASE SOMETHING WENT WRONG.
+!=== ABOUT THE KEYS:
+!     * The "keys" component (of type keys_type) is in principle enough to store everything we could need.
+!     But some variables are stored as direct-access keys to make the code more readable and because they are used often.
+!     * Most of the direct-access keys are set in this module, but some are not (lnam, iadv and itr for example).
+!     * Some of the direct-access keys must be updated (using the routine "setDirectKeys") is a subset of "tracers(:)"
+!     is extracted: the indexes are no longer valid for a subset (examples: tracers(:)%iqParent or tracers(:)%ichld).
+!     * If you need to convert a %key/%val pair into a direct-access key, add the corresponding line in "setDirectKeys".
+!==============================================================================================================================
+LOGICAL FUNCTION readTracersFiles(type_trac, fType, tracs) RESULT(lerr)
+!------------------------------------------------------------------------------------------------------------------------------
+  CHARACTER(LEN=*),             INTENT(IN)  :: type_trac              !--- List of components used
+  INTEGER,                      INTENT(OUT) :: fType                  !--- Type of input file found
+  TYPE(trac_type), ALLOCATABLE, INTENT(OUT) :: tracs(:)
+  CHARACTER(LEN=maxlen),  ALLOCATABLE ::  s(:), sections(:), trac_files(:)
+  CHARACTER(LEN=maxlen) :: str, fname, mesg
+  INTEGER               :: is, nsec, ierr, it, ntrac, ns, ip
+  LOGICAL, ALLOCATABLE  :: ll(:), lGen3(:)
+!------------------------------------------------------------------------------------------------------------------------------
+  lerr = .FALSE.
+  modname = 'readTracersFiles'
+  IF(.NOT.ALLOCATED(dBase)) ALLOCATE(dBase(0))
+
+  !--- Required sections + corresponding files names (new style single section case)
+  IF(test(strParse(type_trac, ',', sections), lerr)) RETURN          !--- Parse "type_trac" list
+
+  nsec = SIZE(sections, DIM=1)
+  ALLOCATE(trac_files(nsec)); DO is=1, nsec; trac_files(is) = 'tracer_'//TRIM(sections(is))//'.def'; END DO
+
+  !--- LOOK AT AVAILABLE FILES
+  ll = .NOT.testFile(trac_files)
+  fType = 0
+  IF(.NOT.testFile('traceur.def') .AND. nsec==1) fType = 1           !--- OLD STYLE FILE
+  IF(.NOT.testFile('tracer.def'))                fType = 2           !--- NEW STYLE ; SINGLE  FILE, SEVERAL SECTIONS
+  IF(ALL(ll))                                    fType = 3           !--- NEW STYLE ; SEVERAL FILES, SINGLE SECTION USED
+  IF(ANY(ll) .AND. fType/=3) THEN                                    !--- MISSING FILES
+    IF(test(checkList(trac_files, .NOT.ll, 'Failed reading tracers description', 'files', 'missing'), lerr)) RETURN
+  END IF
+
+  !--- TELLS WHAT WAS IS ABOUT TO BE USED
+  IF (fmsg('No adequate tracers description file(s) found ; default values will be used',          modname, fType==0)) RETURN
+  CALL msg('Trying to read old-style tracers description file "traceur.def"',                      modname, fType==1)
+  CALL msg('Trying to read the new style multi-sections tracers description file "tracer.def"',    modname, fType==2)
+  CALL msg('Trying to read the new style single section tracers description files "tracer_*.def"', modname, fType==3)
+
+  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+  IF(fType==1) THEN                                                  !=== OLD FORMAT "traceur.def"
+  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+    !--- OPEN THE "traceur.def" FILE
+    OPEN(90, FILE="traceur.def", FORM='formatted', STATUS='old', IOSTAT=ierr)
+
+    !--- GET THE TRACERS NUMBER
+    READ(90,'(i3)',IOSTAT=ierr)ntrac                                 !--- Number of lines/tracers
+    IF(test(fmsg('Invalid format for "'//TRIM(fname)//'"', modname, ierr /= 0), lerr)) RETURN
+
+    !--- READ THE REMAINING LINES: <hadv> <vadv> <tracer> [<transporting fluid>]
+    ALLOCATE(tracs(ntrac))
+    DO it=1,ntrac                                                    !=== READ RAW DATA: loop on the line/tracer number
+      READ(90,'(a)',IOSTAT=ierr) str
+      IF(test(fmsg('Invalid format for "' //TRIM(fname)//'"', modname, ierr>0), lerr)) RETURN
+      IF(test(fmsg('Not enough lines in "'//TRIM(fname)//'"', modname, ierr<0), lerr)) RETURN
+      ll = strParse(str, ' ', s, n=ns)
+      tracs(it)%keys%key = ['hadv', 'vadv']
+      tracs(it)%keys%val = s(1:2)
+      CALL msg('This file is for air tracers only',           modname, ns == 3 .AND. it == 1)
+      CALL msg('This files specifies the transporting fluid', modname, ns == 4 .AND. it == 1)
+      tracs(it)%name = TRIM(s(3))                                    !--- Name of the tracer
+      tracs(it)%phase = known_phases(1:1)                            !--- Phase (default: "g" for gazeous)
+      DO ip = 1, nphases                                             !--- Deal with old water names
+        IF(s(3) /= 'H2O'//old_phases(ip:ip)) CYCLE
+        tracs(it)%phase = known_phases(ip:ip); tracs(it)%name = 'H2O'//phases_sep//TRIM(tracs(it)%phase)
+      END DO
+      tracs(it)%parent = tran0                                       !--- Default transporting fluid: Air
+      IF(ns == 4) tracs(it)%parent = s(4)                            !--- Transporting fluid name
+    END DO
+    CLOSE(90)
+
+    lGen3 = tracs%iGeneration==3
+    CALL setGeneration(tracs)                                        !--- Determine tracs(:)%iGeneration values
+    IF(test(checkTracers(tracs,      fname,fname),lerr)) RETURN      !--- Detect orphans and check phases
+    IF(test(checkUnique (tracs,lGen3,fname,fname),lerr)) RETURN      !--- Detect repeated tracers
+    CALL sortTracers  (tracs)                                        !--- Sort the tracers
+    CALL setDirectKeys(tracs)                                        !--- Set the directly accessible keys
+    tracs(:)%keys%name = tracs(:)%name                               !--- Copy tracers names in keys components
+    RETURN
+  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+  END IF
+  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+  !=== USING NEW FORMAT TRACERS DESCRIPTION FILES WITH POSSIBLY SEVERAL SECTIONS
+  CALL msg('The multiple required sections will be MERGED.',    modname, nsec > 1 .AND.      tracs_merge)
+  CALL msg('The multiple required sections will be CUMULATED.', modname, nsec > 1 .AND. .NOT.tracs_merge)
+
+  !=== FEED THE DATABASE WITH THE RAW CONTENT OF THE FILE
+  SELECT CASE(fType)
+    CASE(2); lerr = feedDBase(["tracer.def"],[type_trac])            !--- Single file, coma-separated sections list
+    CASE(3); lerr = feedDBase(trac_files,     sections  )            !--- Multiple files, one section name each file
+  END SELECT
+  IF(lerr) RETURN
+  IF(     tracs_merge) lerr = mergeTracers(dBase, tracs)             !--- MERGE    THE COMPONENTS OF THE DATABASE
+  IF(.NOT.tracs_merge) lerr = cumulTracers(dBase, tracs)             !--- CUMULATE THE COMPONENTS OF THE DATABASE
+  IF(lerr) RETURN
+  CALL setDirectKeys(tracs)                                          !--- Set the directly accessible keys
+
+END FUNCTION readTracersFiles
+!==============================================================================================================================
+
+!==============================================================================================================================
+LOGICAL FUNCTION feedDBase(fnames, snames) RESULT(lerr)
+! Purpose: Read the sections "snames(is)" (coma-separated list) from each "fnames(is)"
+!   file and create the corresponding tracers set descriptors in the database "dBase":
+! * dBase(id)%name                : section name
+! * dBase(id)%trac(:)%name        : tracers names
+! * dBase(id)%trac(it)%keys%key(:): names  of keys associated to tracer dBase(id)%trac(it)%name
+! * dBase(id)%trac(it)%keys%val(:): values of keys associated to tracer dBase(id)%trac(it)%name
+!------------------------------------------------------------------------------------------------------------------------------
+  CHARACTER(LEN=*), INTENT(IN)  :: fnames(:)                         !--- Files names
+  CHARACTER(LEN=*), INTENT(IN)  :: snames(:)                         !--- Coma-deparated list of sections (one list each file)
+  INTEGER,  ALLOCATABLE :: ndb(:)                                    !--- Nuber of sections for each file
+  INTEGER,  ALLOCATABLE :: ixf(:)                                    !--- File index for each section of the expanded list
+  LOGICAL,  ALLOCATABLE :: lTg(:)                                    !--- Tagging tracers mask
+  CHARACTER(LEN=maxlen) :: fnm, snm, modname
+  INTEGER               :: idb, i
+  LOGICAL :: ll
+!------------------------------------------------------------------------------------------------------------------------------
+  modname = 'feedDBase'
+  !=== READ THE REQUIRED SECTIONS
+  ll = strCount(snames, ',', ndb)                                    !--- Number of sections for each file
+  ALLOCATE(ixf(SUM(ndb)))
+  DO i=1, SIZE(fnames)
+    IF(test(readSections(fnames(i), snames(i), 'default'), lerr)) RETURN
+    ixf(1+SUM(ndb(1:i-1)):SUM(ndb(1:i))) = i                         !--- File index for each section of the expanded list
+  END DO
+  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+  DO idb=1,SIZE(dBase)                                               !--- LOOP ON THE LOADED SECTIONS
+  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+    fnm = fnames(ixf(idb)); snm = dBase(idb)%name
+    IF(test(expandSection(dBase(idb)%trac,   snm, fnm),lerr)) RETURN !--- EXPAND NAMES AND PARENTS LISTS
+    CALL setGeneration   (dBase(idb)%trac)                           !--- DETERMINE GENERATION NUMBER
+    IF(test(checkTracers (dBase(idb)%trac,   snm, fnm),lerr)) RETURN !--- CHECK ORPHANS AND PHASES
+    lTg = dBase(idb)%trac(:)%type == 'tag'                           !--- Flag for tagging tracers
+    IF(test(checkUnique  (dBase(idb)%trac,lTg,snm,fnm),lerr)) RETURN !--- CHECK TRACERS UNIQUENESS
+    CALL expandPhases    (dBase(idb)%trac)                           !--- EXPAND THE PHASES
+    CALL sortTracers     (dBase(idb)%trac)                           !--- SORT TRACERS
+  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+  END DO
+  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+  !=== DISPLAY BASIC INFORMATION
+  lerr = ANY([( dispTraSection('Expanded list for section "'//TRIM(dBase(idb)%name)//'"', dBase(idb)%name, modname), &
+                idb=1, SIZE(dBase) )])
+END FUNCTION feedDBase
+!------------------------------------------------------------------------------------------------------------------------------
+
+!------------------------------------------------------------------------------------------------------------------------------
+LOGICAL FUNCTION readSections(fnam,snam,defName) RESULT(lerr)
+!------------------------------------------------------------------------------------------------------------------------------
+  CHARACTER(LEN=*),           INTENT(IN) :: fnam                     !--- File name
+  CHARACTER(LEN=*),           INTENT(IN) :: snam                     !--- Coma-separated sections list
+  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: defName                  !--- Special section (default values) name
+!------------------------------------------------------------------------------------------------------------------------------
+  TYPE(dataBase_type),   ALLOCATABLE :: tdb(:)
+  CHARACTER(LEN=maxlen), ALLOCATABLE :: sec(:)
+  INTEGER,               ALLOCATABLE ::  ix(:)
+  INTEGER :: n0, idb, ndb, i, j
+  LOGICAL :: ll
+!------------------------------------------------------------------------------------------------------------------------------
+  n0 = SIZE(dBase) + 1                                               !--- Index for next entry in the database
+  CALL readSections_all()                                            !--- Read all the sections of file "fnam"
+  ndb= SIZE(dBase)                                                   !--- Current number of sections in the database
+  IF(PRESENT(defName)) THEN                                          !--- Add default values to all the tracers
+    DO idb=n0,ndb; CALL addDefault(dBase(idb)%trac, defName); END DO !--- and remove the virtual tracer "defName"
+  END IF
+  ll = strParse(snam, ',', keys = sec)                               !--- Requested sections names
+  ix = strIdx(dBase(:)%name, sec(:))                                 !--- Indexes of requested sections in database
+  IF(test(checkList(sec, ix == 0, 'In file "'//TRIM(fnam)//'"','section(s)', 'missing'), lerr)) RETURN
+  tdb = dBase(:); dBase = [tdb(1:n0-1),tdb(PACK(ix, MASK=ix/=0))]    !--- Keep requested sections only
+
+CONTAINS
+
+!------------------------------------------------------------------------------------------------------------------------------
+SUBROUTINE readSections_all()
+!------------------------------------------------------------------------------------------------------------------------------
+  CHARACTER(LEN=maxlen), ALLOCATABLE ::  s(:), v(:)
+  TYPE(trac_type),       ALLOCATABLE :: tt(:)
+  TYPE(trac_type)       :: tmp
+  CHARACTER(LEN=1024)   :: str
+  CHARACTER(LEN=maxlen) :: secn
+  INTEGER               :: ierr, n
+!------------------------------------------------------------------------------------------------------------------------------
+  IF(.NOT.ALLOCATED(dBase)) ALLOCATE(dBase(0))
+  OPEN(90, FILE=fnam, FORM='formatted', STATUS='old')
+  DO; READ(90,'(a)', IOSTAT=ierr)str
+    IF(ierr    /= 0 ) EXIT                                           !--- Finished: error or end of file
+    IF(str(1:1)=='#') CYCLE                                          !--- Skip comments lines
+    CALL removeComment(str)                                          !--- Skip comments at the end of a line
+    IF(str     == '') CYCLE                                          !--- Skip empty line (probably at the end of the file)
+    IF(str(1:1)=='&') THEN                                           !=== SECTION HEADER LINE
+      ndb  = SIZE(dBase)                                             !--- Number of sections so far
+      secn = str(2:LEN_TRIM(str))//' '                               !--- Current section name
+      IF(ANY(dBase(:)%name == secn)) CYCLE                           !--- Already known section
+      IF(secn(1:7) == 'version')     CYCLE                           !--- Skip the "version" special section
+      ndb = ndb + 1                                                  !--- Extend database
+      ALLOCATE(tdb(ndb))
+      tdb(1:ndb-1)  = dBase
+      tdb(ndb)%name = secn
+      ALLOCATE(tdb(ndb)%trac(0))
+      CALL MOVE_ALLOC(FROM=tdb, TO=dBase)
+    ELSE                                                             !=== TRACER LINE
+      ll = strParse(str,' ', keys = s, vals = v, n = n)              !--- Parse <key>=<val> pairs
+      tt = dBase(ndb)%trac(:)
+      tmp%name = s(1); tmp%component=secn; tmp%keys = keys_type(s(1), s(2:n), v(2:n))
+      dBase(ndb)%trac = [tt(:), tmp]
+      DEALLOCATE(tt)
+!      dBase(ndb)%trac = [dBase(ndb)%trac(:), tra(name=s(1), comp=secn, keys=keys_type(s(1), s(2:n), v(2:n)))]
+    END IF
+  END DO
+  CLOSE(90)
+
+END SUBROUTINE readSections_all
+!------------------------------------------------------------------------------------------------------------------------------
+
+END FUNCTION readSections
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+SUBROUTINE addDefault(t, defName)
+!------------------------------------------------------------------------------------------------------------------------------
+! Purpose: Add the keys from virtual tracer named "defName" (if any) and remove this virtual tracer.
+!------------------------------------------------------------------------------------------------------------------------------
+  TYPE(trac_type), ALLOCATABLE, TARGET, INTENT(INOUT) :: t(:)
+  CHARACTER(LEN=*),                     INTENT(IN)    :: defName
+  INTEGER :: jd, it, k
+  TYPE(keys_type), POINTER :: ky
+  TYPE(trac_type), ALLOCATABLE :: tt(:)
+  jd = strIdx(t(:)%name, defName)
+  IF(jd == 0) RETURN
+  ky => t(jd)%keys
+  DO k = 1, SIZE(ky%key)                                             !--- Loop on the keys of the tracer named "defName"
+    CALL addKey_m(ky%key(k), ky%val(k), t(:)%keys)                   !--- Add key to all the tracers (no overwriting)
+  END DO
+  tt = [t(1:jd-1),t(jd+1:SIZE(t))]; CALL MOVE_ALLOC(FROM=tt, TO=t)   !--- Remove the virtual tracer named "defName"
+END SUBROUTINE addDefault
+!==============================================================================================================================
+
+!==============================================================================================================================
+SUBROUTINE subDefault(t, defName, lSubLocal)
+!------------------------------------------------------------------------------------------------------------------------------
+! Purpose: Substitute the keys from virtual tracer named "defName" (if any) and remove this virtual tracer.
+!          Substitute the keys locally (for the current tracer) if the flag "lSubLocal" is .TRUE.
+!------------------------------------------------------------------------------------------------------------------------------
+  TYPE(trac_type), ALLOCATABLE, TARGET, INTENT(INOUT) :: t(:)
+  CHARACTER(LEN=*),                     INTENT(IN)    :: defName
+  LOGICAL,                              INTENT(IN)    :: lSubLocal
+  INTEGER :: i0, it, ik
+  TYPE(keys_type), POINTER     :: k0, ky
+  TYPE(trac_type), ALLOCATABLE :: tt(:)
+  i0 = strIdx(t(:)%name, defName)
+  IF(i0 == 0) RETURN
+  k0 => t(i0)%keys 
+  DO it = 1, SIZE(t); IF(it == i0) CYCLE                             !--- Loop on the tracers
+    ky => t(it)%keys
+
+    !--- Substitute in the values of <key>=<val> pairs the keys defined in the virtual tracer "defName"
+    DO ik = 1, SIZE(k0%key); CALL strReplace(ky%val, k0%key(ik), k0%val(ik), .TRUE.); END DO
+
+    IF(.NOT.lSubLocal) CYCLE
+    !--- Substitute in the values of <key>=<val> pairs the keys defined locally (in the current tracer)
+    DO ik = 1, SIZE(ky%key); CALL strReplace(ky%val, ky%key(ik), ky%val(ik), .TRUE.); END DO
+  END DO
+  tt = [t(1:i0-1),t(i0+1:SIZE(t))]; CALL MOVE_ALLOC(FROM=tt, TO=t)   !--- Remove the virtual tracer named "defName"
+
+END SUBROUTINE subDefault
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+LOGICAL FUNCTION expandSection(tr, sname, fname) RESULT(lerr)
+!------------------------------------------------------------------------------------------------------------------------------
+! Purpose: Expand tracers and parents lists in the tracers descriptor "tra".
+! Note:  * The following keys are expanded, so are accessible explicitely using "%" operator: "parent" "type".
+!        * Default values are provided for these keys because they are necessary.
+!------------------------------------------------------------------------------------------------------------------------------
+  TYPE(trac_type), ALLOCATABLE, INTENT(INOUT) :: tr(:)                 !--- Tracer derived type vector
+  CHARACTER(LEN=*),             INTENT(IN)    :: sname
+  CHARACTER(LEN=*), OPTIONAL,   INTENT(IN)    :: fname
+  TYPE(trac_type),       ALLOCATABLE :: ttr(:)
+  CHARACTER(LEN=maxlen), ALLOCATABLE :: ta(:), pa(:)
+  CHARACTER(LEN=maxlen) :: msg1, modname
+  INTEGER :: it, nt, iq, nq, itr, ntr, ipr, npr, i
+  LOGICAL :: ll
+  modname = 'expandSection'
+  lerr = .FALSE.
+  nt = SIZE(tr)
+  nq = 0
+  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+  DO it = 1, nt    !=== GET TRACERS NB AFTER EXPANSION + NEEDED KEYS (name, parent, type)
+  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+    !--- Extract useful keys: parent name, type, component name
+    tr(it)%parent    = fgetKey(it, 'parent', tr(:)%keys,  tran0  )
+    tr(it)%type      = fgetKey(it, 'type'  , tr(:)%keys, 'tracer')
+    tr(it)%component = sname
+
+    !--- Determine the number of tracers and parents ; coherence checking
+    ll = strCount(tr(it)%name,   ',', ntr)
+    ll = strCount(tr(it)%parent, ',', npr)
+
+    !--- Tagging tracers only can have multiple parents
+    IF(test(npr/=1 .AND. TRIM(tr(it)%type)/='tag', lerr)) THEN
+      msg1 = 'Check section "'//TRIM(sname)//'"'
+      IF(PRESENT(fname)) msg1=TRIM(msg1)//' in file "'//TRIM(fname)//'"'
+      CALL msg(TRIM(msg1)//': "'//TRIM(tr(it)%name)//'" has several parents but is not a tag', modname); RETURN
+    END IF
+    nq = nq + ntr*npr                 
+  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+  END DO
+  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+  CALL delKey(['parent','type  '], tr)
+
+  ALLOCATE(ttr(nq))
+  iq = 1
+  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+  DO it = 1, nt                                                      !=== EXPAND TRACERS AND PARENTS NAMES LISTS
+  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+    ll = strParse(tr(it)%name, ',', ta, n=ntr)                       !--- Number of tracers
+    ll = strParse(tr(it)%parent, ',', pa, n=npr)                     !--- Number of parents
+    DO ipr=1,npr                                                     !--- Loop on parents list elts
+      DO itr=1,ntr                                                   !--- Loop on tracers list elts
+        i = iq+itr-1+(ipr-1)*ntr
+        ttr(i)%name = TRIM(ta(itr)); ttr(i)%parent = pa(ipr)
+        ttr(i)%keys = keys_type(ta(itr), tr(it)%keys%key, tr(it)%keys%val)
+      END DO
+    END DO
+    ttr(iq:iq+ntr*npr-1)%type = tr(it)%type                          !--- Duplicating type
+    iq = iq + ntr*npr
+  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+  END DO
+  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+  DEALLOCATE(ta,pa)
+  CALL MOVE_ALLOC(FROM=ttr, TO=tr)
+
+END FUNCTION expandSection
+!==============================================================================================================================
+
+!==============================================================================================================================
+SUBROUTINE setGeneration(tr)
+!------------------------------------------------------------------------------------------------------------------------------
+! Purpose: Determine, for each tracer of "tr(:)":
+!   * the generation number 
+!   * the first generation ancestor name
+!------------------------------------------------------------------------------------------------------------------------------
+! Arguments:
+  TYPE(trac_type), INTENT(INOUT) :: tr(:)                            !--- Tracer derived type vector
+!------------------------------------------------------------------------------------------------------------------------------
+! Local variables:
+  INTEGER :: iq, nq, ig
+  LOGICAL,               ALLOCATABLE :: lg(:)
+  CHARACTER(LEN=maxlen), ALLOCATABLE :: prn(:)
+!------------------------------------------------------------------------------------------------------------------------------
+  tr(:)%iGeneration = 0                                              !--- error if 0
+  nq = SIZE(tr, DIM=1)                                               !--- Number of tracers lines
+  lg = tr(:)%parent == tran0                                         !--- First generation tracers flag
+  WHERE(lg) tr(:)%iGeneration = 1                                    !--- First generation tracers
+
+  !=== Determine generation for each tracer
+  ig=0; prn = [tran0]
+  DO                                                                 !--- Update current generation flag
+    IF(ig/=0) prn = PACK( tr(:)%name, MASK=tr(:)%iGeneration == ig)
+    lg(:) = [(ANY(prn(:) == tr(iq)%parent), iq=1, nq)]               !--- Current generation tracers flag
+    IF( ALL( .NOT. lg ) ) EXIT                                       !--- Empty current generation
+    ig = ig+1; WHERE(lg) tr(:)%iGeneration = ig
+  END DO
+  tr(:)%gen0Name = ancestor(tr)                                      !--- First generation ancestor name
+
+END SUBROUTINE setGeneration
+!==============================================================================================================================
+
+!==============================================================================================================================
+LOGICAL FUNCTION checkTracers(tr, sname, fname) RESULT(lerr)
+!------------------------------------------------------------------------------------------------------------------------------
+! Purpose:
+!   * check for orphan tracers (without known parent)
+!   * check wether the phases are known or not ("g"aseous, "l"iquid or "s"olid so far)
+!------------------------------------------------------------------------------------------------------------------------------
+  TYPE(trac_type),            INTENT(IN) :: tr(:)                    !--- Tracer derived type vector
+  CHARACTER(LEN=*),           INTENT(IN) :: sname                    !--- Section name
+  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: fname                    !--- File name
+  CHARACTER(LEN=maxlen) :: mesg
+  CHARACTER(LEN=maxlen) :: bp(SIZE(tr, DIM=1)), pha                  !--- Bad phases list, phases of current tracer
+  CHARACTER(LEN=1) :: p
+  INTEGER :: ip, np, iq, nq
+!------------------------------------------------------------------------------------------------------------------------------
+  nq = SIZE(tr,DIM=1)                                                !--- Number of tracers lines
+  mesg = 'Check section "'//TRIM(sname)//'"'
+  IF(PRESENT(fname)) mesg=TRIM(mesg)//' in file "'//TRIM(fname)//'"'
+
+  !=== CHECK FOR ORPHAN TRACERS
+  IF(test(checkList(tr%name, tr%iGeneration==0, mesg, 'tracers', 'orphan'), lerr)) RETURN
+
+  !=== CHECK PHASES
+  DO iq=1,nq; IF(tr(iq)%iGeneration/=1) CYCLE                        !--- Generation 1 only is checked
+    pha = fgetKey(iq, 'phases', tr(:)%keys, 'g')                     !--- Phases
+    np = LEN_TRIM(pha); bp(iq)=' '
+    DO ip=1,np; p = pha(ip:ip); IF(INDEX(known_phases,p)==0) bp(iq) = TRIM(bp(iq))//p; END DO
+    IF(TRIM(bp(iq)) /= '') bp(iq) = TRIM(tr(iq)%name)//': '//TRIM(bp(iq))
+  END DO
+  lerr = checkList(bp, tr%iGeneration==1 .AND. bp/='', mesg, 'tracers phases', 'unknown')
+END FUNCTION checkTracers
+!==============================================================================================================================
+
+!==============================================================================================================================
+LOGICAL FUNCTION checkUnique(tr, lTag, sname, fname) RESULT(lerr)
+!------------------------------------------------------------------------------------------------------------------------------
+! Purpose: Make sure that tracers are not repeated.
+!------------------------------------------------------------------------------------------------------------------------------
+  TYPE(trac_type),            INTENT(IN) :: tr(:)                    !--- Tracer derived type vector
+  LOGICAL,                    INTENT(IN) :: lTag(:)                  !--- Tagging tracer flag
+  CHARACTER(LEN=*),           INTENT(IN) :: sname                    !--- Section name
+  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: fname                    !--- File name
+!------------------------------------------------------------------------------------------------------------------------------
+  INTEGER :: ip, np, iq, nq, k
+  LOGICAL, ALLOCATABLE  :: ll(:)
+  CHARACTER(LEN=maxlen) :: mesg, tnam, tdup(SIZE(tr,DIM=1))
+  CHARACTER(LEN=1)      :: p
+!------------------------------------------------------------------------------------------------------------------------------
+  mesg = 'Check section "'//TRIM(sname)//'"'
+  IF(PRESENT(fname)) mesg=TRIM(mesg)//' in file "'//TRIM(fname)//'"'
+  nq=SIZE(tr,DIM=1); lerr=.FALSE.                                    !--- Number of lines ; error flag
+  tdup(:) = ''
+  DO iq=1,nq; IF(lTag(iq)) CYCLE                                     !--- Tags can be repeated
+    tnam = TRIM(tr(iq)%name)
+    ll = tr(:)%name==TRIM(tnam)                                      !--- Mask for current tracer name
+    IF(COUNT(ll)==1 ) CYCLE                                          !--- Tracer is not repeated
+    IF(tr(iq)%iGeneration>1) THEN
+      tdup(iq) = tnam                                                !--- gen>1: MUST be unique
+    ELSE
+      DO ip=1,nphases; p=known_phases(ip:ip)                         !--- Loop on known phases
+        !--- Number of appearances of the current tracer with known phase "p"
+        np = COUNT( PACK( [(INDEX(fgetKey(k, 'phases', tr(:)%keys, 'g'),p), k=1, nq)] /=0 , MASK=ll ) )
+        IF(np <=1) CYCLE
+        tdup(iq) = TRIM(tdup(iq))//TRIM(phases_names(ip))
+        IF(ANY(tdup(1:iq-1) == tdup(iq))) tdup(iq)=''                !--- Avoid repeating same messages
+      END DO
+    END IF
+    IF(tdup(iq) /= '') tdup(iq)=TRIM(tnam)//' in '//TRIM(tdup(iq))//' phase(s)'
+  END DO
+  lerr = checkList(tdup, tdup/='', mesg, 'tracers', 'duplicated')
+END FUNCTION checkUnique
+!==============================================================================================================================
+
+!==============================================================================================================================
+SUBROUTINE expandPhases(tr)
+!------------------------------------------------------------------------------------------------------------------------------
+! Purpose: Expand the phases in the tracers descriptor "tr".
+!------------------------------------------------------------------------------------------------------------------------------
+  TYPE(trac_type), ALLOCATABLE, INTENT(INOUT) :: tr(:)               !--- Tracer derived type vector
+!------------------------------------------------------------------------------------------------------------------------------
+  TYPE(trac_type), ALLOCATABLE :: ttr(:)
+  INTEGER,   ALLOCATABLE ::  i0(:)
+  CHARACTER(LEN=maxlen)  :: nam, pha, trn
+  INTEGER :: ip, np, iq, jq, nq, it, nt, nc, i, n
+  LOGICAL :: lTg, lEx
+!------------------------------------------------------------------------------------------------------------------------------
+  nq = SIZE(tr, DIM=1)
+  nt = 0
+  DO iq = 1, nq                                                      !--- GET THE NUMBER OF TRACERS
+    IF(tr(iq)%iGeneration /= 1) CYCLE
+    nc = COUNT(tr(:)%gen0Name==tr(iq)%name .AND. tr%iGeneration/=1)  !--- Number of childs of tr(iq)
+    tr(iq)%phase = fgetKey(iq, 'phases', tr(:)%keys)                 !--- Phases list      of tr(iq)
+    np = LEN_TRIM(tr(iq)%phase)                                      !--- Number of phases of tr(iq)
+    nt = nt + (1+nc) * np                                            !--- Number of tracers after expansion
+  END DO
+  ALLOCATE(ttr(nt))
+  it = 1                                                             !--- Current "ttr(:)" index
+  DO iq = 1, nq                                                      !--- Loop on "tr(:)" indexes
+    lTg = tr(iq)%type=='tag'                                         !--- Current tracer is a tag
+    i0 = strFind(tr(:)%name, TRIM(tr(iq)%gen0Name), n)               !--- Indexes of first generation ancestor copies
+    np = SUM( [( LEN_TRIM(tr(i0(i))%phase),i=1,n )],1)               !--- Number of phases for current tracer tr(iq)
+    lEx = np>1                                                       !--- Need of a phase suffix
+    IF(lTg) lEx=lEx.AND.tr(iq)%iGeneration>1                         !--- No phase suffix for first generation tags
+    DO i=1,n                                                         !=== LOOP ON FIRST GENERATION ANCESTORS
+      jq=i0(i)                                                       !--- tr(jq): ith copy of 1st gen. ancestor of tr(iq)
+      IF(tr(iq)%iGeneration==1) jq=iq                                !--- Generation 1: current tracer phases only
+      pha = tr(jq)%phase                                             !--- Phases list for tr(jq)
+      DO ip=1,LEN_TRIM(pha)                                          !=== LOOP ON PHASES LISTS
+        trn=TRIM(tr(iq)%name); nam=trn                               !--- Tracer name (regular case)
+        IF(lTg) nam = TRIM(tr(iq)%parent)                            !--- Parent name (tagging case)
+        IF(lEx) nam = TRIM(nam)//phases_sep//pha(ip:ip)              !--- Phase extension needed
+        IF(lTg) nam = TRIM(nam)//'_'//TRIM(trn)                      !--- <parent>_<name> for tags
+        ttr(it) = tr(iq)                                             !--- Same <key>=<val> pairs
+        ttr(it)%name = TRIM(nam)                                     !--- Name with possibly phase suffix
+        ttr(it)%keys%name = TRIM(nam)                                !--- Name inside the keys decriptor
+        ttr(it)%phase = pha(ip:ip)                                   !--- Single phase entry
+        IF(lEx.AND.tr(iq)%iGeneration>1) THEN
+          ttr(it)%parent   = TRIM(ttr(it)%parent)//phases_sep//pha(ip:ip)
+          ttr(it)%gen0Name = TRIM(ttr(it)%gen0Name)//phases_sep//pha(ip:ip)
+        END IF
+        it=it+1
+      END DO
+      IF(tr(iq)%iGeneration==1) EXIT                                 !--- Break phase loop for gen 1
+    END DO
+  END DO
+  CALL MOVE_ALLOC(FROM=ttr, TO=tr)
+  CALL delKey(['phases'],tr)                                         !--- Remove few keys entries
+
+END SUBROUTINE expandPhases
+!==============================================================================================================================
+
+!==============================================================================================================================
+SUBROUTINE sortTracers(tr)
+!------------------------------------------------------------------------------------------------------------------------------
+! Purpose: Sort tracers:
+!  * lGrowGen == T: in ascending generations numbers.
+!  * lGrowGen == F: tracer + its childs sorted by growing generation, one after the other.
+!------------------------------------------------------------------------------------------------------------------------------
+  TYPE(trac_type), ALLOCATABLE, INTENT(INOUT) :: tr(:)               !--- Tracer derived type vector
+  INTEGER :: ig, ng, iq, jq, n, ix(SIZE(tr)), k
+  INTEGER, ALLOCATABLE :: iy(:), iz(:)
+!------------------------------------------------------------------------------------------------------------------------------
+  iq = 1
+  IF(lSortByGen) THEN
+    ng = MAXVAL(tr(:)%iGeneration, MASK=.TRUE., DIM=1)               !--- Number of generations
+    DO ig = 0, ng                                                    !--- Loop on generations
+      iy = PACK([(k, k=1, SIZE(tr))], MASK=tr(:)%iGeneration==ig)    !--- Generation ig tracers indexes
+      n = SIZE(iy)
+      ix(iq:iq+n-1) = iy                                             !--- Stack growing generations idxs
+      iq = iq + n
+    END DO
+  ELSE
+    DO jq = 1, SIZE(tr,DIM=1)                                        !--- Loop on first generation tracers
+      IF(tr(jq)%iGeneration /= 1) CYCLE                              !--- Skip generations >= 1
+      ix(iq) = jq                                                    !--- First generation ancestor index first
+      iq = iq + 1
+      iy = strFind(tr(:)%gen0Name, TRIM(tr(jq)%name))                !--- Indexes of "tr(jq)" childs in "tr(:)"
+      ng = MAXVAL(tr(iy)%iGeneration, MASK=.TRUE., DIM=1)            !--- Generations number of the "tr(jq)" family
+      DO ig = 2, ng                                                  !--- Loop   on generations for the tr(jq) family
+        iz = find(tr(iy)%iGeneration, ig, n)                         !--- Indexes of the tracers "tr(iy(:))" of generation "ig"
+        ix(iq:iq+n-1) = iy(iz)                                       !--- Same indexes in "tr(:)"
+        iq = iq + n
+      END DO
+    END DO
+  END IF
+  tr = tr(ix)                                                        !--- Reorder the tracers
+END SUBROUTINE sortTracers
+!==============================================================================================================================
+
+!==============================================================================================================================
+LOGICAL FUNCTION mergeTracers(sections, tr) RESULT(lerr)
+  TYPE(dataBase_type),  TARGET, INTENT(IN)  :: sections(:)
+  TYPE(trac_type), ALLOCATABLE, INTENT(OUT) ::       tr(:)
+  TYPE(trac_type), POINTER ::   t1(:),   t2(:)
+  INTEGER,     ALLOCATABLE :: ixct(:), ixck(:)
+  INTEGER :: is, k1, k2, nk2, i1, i2, nt2
+  CHARACTER(LEN=maxlen) :: s1, v1, v2, tnam, knam, modname
+  modname = 'mergeTracers'
+  lerr = .FALSE.
+  t1 => sections(1)%trac(:)                                          !--- Alias: first tracers section
+  tr = t1
+  !----------------------------------------------------------------------------------------------------------------------------
+  DO is=2,SIZE(sections)                                             !=== SEVERAL SECTIONS: MERGE THEM
+  !----------------------------------------------------------------------------------------------------------------------------
+    t2  => sections(is)%trac(:)                                      !--- Alias: current tracers section
+    nt2  = SIZE(t2(:), DIM=1)                                        !--- Number of tracers in section
+    ixct = strIdx(t1(:)%name, t2(:)%name)                            !--- Indexes of common tracers
+    tr = [tr, PACK(t2, MASK= ixct==0)]                               !--- Append with new tracers
+    IF( ALL(ixct == 0) ) CYCLE                                       !--- No common tracers => done
+    CALL msg('Tracers defined in previous sections and duplicated in "'//TRIM(sections(is)%name)//'":', modname)
+    CALL msg(t1(PACK(ixct, MASK = ixct/=0))%name, modname, nmax=128) !--- Display duplicates (the 128 first at most)
+    !--------------------------------------------------------------------------------------------------------------------------
+    DO i2=1,nt2; tnam = TRIM(t2(i2)%name)                            !=== LOOP ON COMMON TRACERS
+    !--------------------------------------------------------------------------------------------------------------------------
+      i1 = ixct(i2); IF(i1 == 0) CYCLE                               !--- Idx in t1(:) ; skip new tracers
+
+      !=== CHECK WETHER ESSENTIAL KEYS ARE IDENTICAL OR NOT
+      s1=' of "'//TRIM(tnam)//'" in "'//TRIM(sections(is)%name)//'" not matching previous value'
+      
+      IF(test(fmsg('Parent name'//TRIM(s1), modname, t1(i1)%parent      /= t2(i2)%parent),      lerr)) RETURN
+      IF(test(fmsg('Type'       //TRIM(s1), modname, t1(i1)%type        /= t2(i2)%type),        lerr)) RETURN
+      IF(test(fmsg('Generation' //TRIM(s1), modname, t1(i1)%iGeneration /= t2(i2)%iGeneration), lerr)) RETURN
+
+      !=== APPEND <key>=<val> PAIRS NOT PREVIOULSLY DEFINED
+      nk2  = SIZE(t2(i2)%keys%key(:))                                !--- Keys number in current section
+      ixck = strIdx(t1(i1)%keys%key(:), t2(i2)%keys%key(:))          !--- Common keys indexes
+
+      !=== APPEND NEW KEYS
+      tr(i1)%keys%key = [ tr(i1)%keys%key, PACK(tr(i2)%keys%key, MASK = ixck==0)]
+      tr(i1)%keys%val = [ tr(i1)%keys%val, PACK(tr(i2)%keys%val, MASK = ixck==0)]
+
+      !--- KEEP TRACK OF THE COMPONENTS NAMES
+      tr(i1)%component = TRIM(tr(i1)%component)//','//TRIM(tr(i2)%component)
+
+      !--- SELECT COMMON TRACERS WITH DIFFERING KEYS VALUES (PREVIOUS VALUE IS KEPT)
+      DO k2=1,nk2
+        k1 = ixck(k2); IF(k1 == 0) CYCLE
+        IF(t1(i1)%keys%val(k1) == t2(i2)%keys%val(k2)) ixck(k2)=0
+      END DO
+      IF(ALL(ixck==0)) CYCLE                                         !--- No identical keys with /=values
+
+      !--- DISPLAY INFORMATION: OLD VALUES ARE KEPT FOR THE KEYS FOUND IN PREVIOUS AND CURRENT SECTIONS
+      CALL msg('Key(s)'//TRIM(s1), modname)
+      DO k2 = 1, nk2                                                 !--- Loop on keys found in both t1(:) and t2(:)
+        knam = t2(i2)%keys%key(k2)                                   !--- Name of the current key
+        k1 = ixck(k2)                                                !--- Corresponding index in t1(:)
+        IF(k1 == 0) CYCLE                                            !--- New keys are skipped
+        v1 = t1(i1)%keys%val(k1); v2 = t2(i2)%keys%val(k2)           !--- Key values in t1(:) and t2(:)
+        CALL msg(' * '//TRIM(knam)//'='//TRIM(v2)//' ; previous value kept:'//TRIM(v1), modname)
+      END DO
+      !------------------------------------------------------------------------------------------------------------------------
+    END DO
+    !--------------------------------------------------------------------------------------------------------------------------
+  END DO
+  CALL sortTracers(tr)
+
+END FUNCTION mergeTracers
+!==============================================================================================================================
+
+!==============================================================================================================================
+LOGICAL FUNCTION cumulTracers(sections, tr) RESULT(lerr)
+  TYPE(dataBase_type),  TARGET, INTENT(IN)  :: sections(:)
+  TYPE(trac_type), ALLOCATABLE, INTENT(OUT) ::       tr(:)
+  TYPE(trac_type), POINTER     :: t1(:), t2(:)
+  INTEGER,   ALLOCATABLE :: nt(:)
+  CHARACTER(LEN=maxlen)  :: tnam, tnam_new
+  INTEGER :: iq, nq, is, ns, nsec
+  lerr = .FALSE.                                                     !--- Can't fail ; kept to match "mergeTracer" interface.
+  nsec =  SIZE(sections)
+  tr = [(      sections(is)%trac(:) , is=1, nsec )]                  !--- Concatenated tracers vector
+  nt = [( SIZE(sections(is)%trac(:)), is=1, nsec )]                  !--- Number of tracers in each section
+  !----------------------------------------------------------------------------------------------------------------------------
+  DO is=1, nsec                                                      !=== LOOP ON SECTIONS
+  !----------------------------------------------------------------------------------------------------------------------------
+    t1 => sections(is)%trac(:)
+    !--------------------------------------------------------------------------------------------------------------------------
+    DO iq=1, nt(is)                                                  !=== LOOP ON TRACERS
+    !--------------------------------------------------------------------------------------------------------------------------
+      tnam = TRIM(t1(iq)%name)                                       !--- Original name
+      IF(COUNT(t1%name == tnam) == 1) CYCLE                          !--- Current tracer is not duplicated: finished
+      tnam_new = TRIM(tnam)//phases_sep//TRIM(sections(is)%name)     !--- Same with section extension
+      nq = SUM(nt(1:is-1))                                           !--- Number of tracers in previous sections
+      ns = nt(is)                                                    !--- Number of tracers in the current section
+      tr(iq + nq)%name = TRIM(tnam_new)                              !--- Modify tracer name
+      WHERE(tr(1+nq:ns+nq)%parent==tnam) tr(1+nq:ns+nq)%parent=tnam_new  !--- Modify parent name
+    !--------------------------------------------------------------------------------------------------------------------------
+    END DO
+  !----------------------------------------------------------------------------------------------------------------------------
+  END DO
+  !----------------------------------------------------------------------------------------------------------------------------
+  CALL sortTracers(tr)
+END FUNCTION cumulTracers
+!==============================================================================================================================
+
+!==============================================================================================================================
+SUBROUTINE setDirectKeys(tr)
+  TYPE(trac_type), INTENT(INOUT) :: tr(:)
+  CALL indexUpdate(tr)                                               !--- Update iqParent and iqDescen indexes vectors
+!  DO iq = 1, SIZE(tr)
+!    tr(iq)%keys%<key> = getKey_prv(it, "<key>", tr%keys,  tran0 )   !--- For additional keys
+!  END DO
+END SUBROUTINE setDirectKeys
+!==============================================================================================================================
+
+!==============================================================================================================================
+LOGICAL FUNCTION dispTraSection(message, sname, modname) RESULT(lerr)
+  CHARACTER(LEN=*), INTENT(IN) :: message, sname, modname
+  INTEGER :: idb, iq, nq
+  INTEGER, ALLOCATABLE :: hadv(:), vadv(:)
+  TYPE(trac_type), POINTER :: tm(:)
+  lerr = .FALSE.
+  idb = strIdx(dBase(:)%name, sname); IF(idb == 0) RETURN
+  tm => dBase(idb)%trac
+  nq = SIZE(tm)
+  IF(test(getKeyByName_im('hadv', hadv, tm(:)%name, tm(:)%keys),lerr)) RETURN
+  IF(test(getKeyByName_im('vadv', vadv, tm(:)%name, tm(:)%keys),lerr)) RETURN
+  CALL msg(TRIM(message)//':', modname)
+  IF(test(dispTable('iiissis', ['iq        ','hadv      ','vadv      ','short name','parent    ','igen      ','phase     '], &
+    cat(tm(:)%name,  tm(:)%parent, tm(:)%phase), cat([(iq, iq=1, nq)],  hadv,  vadv, tm(:)%iGeneration)), lerr)) RETURN
+END FUNCTION dispTraSection
+!==============================================================================================================================
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+!== CREATE A SCALAR ALIAS OF THE COMPONENT OF THE TRACERS DESCRIPTOR "t" NAMED "tname" ========================================
+!==============================================================================================================================
+FUNCTION aliasTracer(tname, t) RESULT(out)
+  TYPE(trac_type),         POINTER    :: out
+  CHARACTER(LEN=*),        INTENT(IN) :: tname
+  TYPE(trac_type), TARGET, INTENT(IN) :: t(:)
+  INTEGER :: it
+  it = strIdx(t(:)%name, tname)
+  out => NULL(); IF(it /= 0) out => t(it)
+END FUNCTION aliasTracer
+!------------------------------------------------------------------------------------------------------------------------------
+
+
+!==============================================================================================================================
+!=== FROM A LIST OF INDEXES OR NAMES, CREATE A SUBSET OF THE TRACERS DESCRIPTORS LIST "trac" ==================================
+!==============================================================================================================================
+FUNCTION trSubset_Indx(trac,idx) RESULT(out)
+  TYPE(trac_type), ALLOCATABLE             ::  out(:)
+  TYPE(trac_type), ALLOCATABLE, INTENT(IN) :: trac(:)
+  INTEGER,                      INTENT(IN) ::  idx(:)
+  out = trac(idx)
+  CALL indexUpdate(out)
+END FUNCTION trSubset_Indx
+!------------------------------------------------------------------------------------------------------------------------------
+FUNCTION trSubset_Name(trac,nam) RESULT(out)
+  TYPE(trac_type), ALLOCATABLE             ::  out(:)
+  TYPE(trac_type), ALLOCATABLE, INTENT(IN) :: trac(:)
+  CHARACTER(LEN=*),             INTENT(IN) ::  nam(:)
+  out = trac(strIdx(trac(:)%name, nam))
+  CALL indexUpdate(out)
+END FUNCTION trSubset_Name
+!------------------------------------------------------------------------------------------------------------------------------
+
+
+!==============================================================================================================================
+!=== CREATE THE SUBSET OF THE TRACERS DESCRIPTORS LIST "trac" HAVING THE FIRST GENERATION ANCESTOR NAMED "nam" ================
+!==============================================================================================================================
+FUNCTION trSubset_gen0Name(trac,nam) RESULT(out)
+  TYPE(trac_type), ALLOCATABLE             ::  out(:)
+  TYPE(trac_type), ALLOCATABLE, INTENT(IN) :: trac(:)
+  CHARACTER(LEN=*),             INTENT(IN) ::  nam
+  out = trac(strFind(delPhase(trac(:)%gen0Name), nam))
+  CALL indexUpdate(out)
+END FUNCTION trSubset_gen0Name
+!------------------------------------------------------------------------------------------------------------------------------
+
+
+!==============================================================================================================================
+!=== UPDATE THE INDEXES iqParent, iqDescend AND iGeneration IN THE TRACERS DESCRIPTOR LIST "tr" (USEFULL FOR SUBSETS) =========
+!==============================================================================================================================
+SUBROUTINE indexUpdate(tr)
+  TYPE(trac_type), INTENT(INOUT) :: tr(:)
+  INTEGER :: iq, ig, ng, ngen
+  INTEGER, ALLOCATABLE :: ix(:)
+  tr(:)%iqParent = strIdx( tr(:)%name, tr(:)%parent )                !--- Parent index
+  ngen = MAXVAL(tr(:)%iGeneration, MASK=.TRUE.)
+  DO iq = 1, SIZE(tr)
+    ng = tr(iq)%iGeneration                                          !--- Generation of the current tracer
+    ix = idxAncestor(tr, igen = ng); ix = PACK(ix, ix/=0)            !--- Indexes of the tracers with ancestor tr(iq)
+    !--- Childs indexes in growing generation order
+    tr(iq)%iqDescen = [( PACK(ix, MASK = tr(ix)%iGeneration == ig), ig = ng+1, ngen)]
+    tr(iq)%nqDescen =     SUM(  [( COUNT(tr(ix)%iGeneration == ig), ig = ng+1, ngen)] )
+    tr(iq)%nqChilds =              COUNT(tr(ix)%iGeneration == ng+1)
+  END DO
+END SUBROUTINE indexUpdate
+!------------------------------------------------------------------------------------------------------------------------------
+  
+  
+!==============================================================================================================================
+!=== READ FILE "fnam" TO APPEND THE "dBase" TRACERS DATABASE WITH AS MUCH SECTIONS AS PARENTS NAMES IN "isot(:)%parent":   ====
+!===  * Each section dBase(i)%name contains the isotopes "dBase(i)%trac(:)" descending on "dBase(i)%name"="iso(i)%parent"  ====
+!===  * For each isotopes class, the <key>=<val> vector of each tracer is moved into the isotopes descriptor "isot"        ====
+!=== NOTES:                                                                                                                ====
+!===  * Most of the "isot" components have been defined in the calling routine (initIsotopes):                             ====
+!===      prnt,   nzon, zone(:),   niso, keys(:)%name,   nitr, trac(:),   npha, phas,  iTraPha(:,:),  iZonPhi(:,:)         ====
+!===  * Same syntax for isotopes file and "tracer.def": a tracers section contains one line for each of its isotopes       ====
+!===  * Each tracers section can contain a "params" virtual isotope line of isotopes parameters default values             ====
+!===  * In case keys are found both in the "params" section and the "*.def" file, the later value is retained              ====
+!===  * On each isotope line, defined keys can be used for other keys defintions (single level depth substitution)         ====
+!===  * The routine gives an error if a required isotope is not available in the database stored in "fnam"                 ====
+!==============================================================================================================================
+LOGICAL FUNCTION readIsotopesFile(fnam, isot) RESULT(lerr)
+  CHARACTER(LEN=*),        INTENT(IN)    :: fnam                     !--- Input file name
+  TYPE(isot_type), TARGET, INTENT(INOUT) :: isot(:)                  !--- Isotopes descriptors (field "prnt" must be defined !)
+  INTEGER :: ik, is, it, idb, nk0, i, iis
+  INTEGER :: nk, ns, nt, ndb, nb0, i0
+  CHARACTER(LEN=maxlen), POINTER     :: k(:), v(:), k0(:), v0(:)
+  CHARACTER(LEN=maxlen), ALLOCATABLE :: vals(:)
+  CHARACTER(LEN=maxlen)              :: val, modname
+  TYPE(keys_type),           POINTER ::   ky(:)
+  TYPE(trac_type),           POINTER ::   tt(:), t
+  TYPE(dataBase_type),   ALLOCATABLE ::  tdb(:)
+  LOGICAL,               ALLOCATABLE :: liso(:)
+  modname = 'readIsotopesFile'
+
+  !--- THE INPUT FILE MUST BE PRESENT
+  IF(test(fmsg('Missing isotopes parameters file "'//TRIM(fnam)//'"', modname, testFile(fnam)),lerr)) RETURN
+
+  !--- READ THE FILE SECTIONS, ONE EACH PARENT TRACER
+  nb0 = SIZE(dBase, DIM=1)+1                                         !--- Next database element index
+  IF(test(readSections(fnam,strStack(isot(:)%parent,',')),lerr)) RETURN !--- Read sections, one each parent tracer
+  ndb = SIZE(dBase, DIM=1)                                           !--- Current database size
+  DO idb = nb0, ndb
+   iis = idb-nb0+1
+
+    !--- GET FEW GLOBAL KEYS FROM "def" FILES AND ADD THEM TO THE 'params' SECTION
+    CALL addKeysFromDef(dBase(idb)%trac, 'params')
+
+    !--- SUBSTITUTE THE KEYS DEFINED IN THE 'params' VIRTUAL TRACER ; SUBSTITUTE LOCAL KEYS ; REMOVE 'params' VIRTUAL TRACER
+    CALL subDefault(dBase(idb)%trac, 'params', .TRUE.)
+
+    tt => dBase(idb)%trac
+
+    !--- REDUCE THE EXPRESSIONS TO OBTAIN SCALARS AND TRANSFER THEM TO THE "isot" ISOTOPES DESCRIPTORS VECTOR
+    DO it = 1, SIZE(dBase(idb)%trac)
+      t => dBase(idb)%trac(it)
+      is = strIdx(isot(iis)%keys(:)%name, t%name)                    !--- Index in "isot(iis)%keys(:)%name" of isotope "t%name"
+      IF(is == 0) CYCLE
+      liso = reduceExpr(t%keys%val, vals)                            !--- Reduce expressions (for substituted variables)
+      IF(test(ANY(liso), lerr)) RETURN                               !--- Some non-numerical elements were found
+      isot(iis)%keys(is)%key = PACK(t%keys%key, MASK=.NOT.liso)
+      isot(iis)%keys(is)%val = PACK(  vals,     MASK=.NOT.liso)
+    END DO
+
+    !--- CHECK FOR MISSING ISOTOPES (NO KEYS ALLOCATED)
+    liso = [( ALLOCATED(isot(iis)%keys(is)%key), is=1, SIZE(isot(iis)%keys) )]
+    IF(test(checkList(isot(iis)%keys(:)%name, .NOT.liso, &
+      'Check file "'//TRIM(fnam)//'" in section "'//TRIM(dBase(idb)%name)//'"', 'isotopes', 'missing'),lerr)) RETURN
+  END DO
+
+  !--- CLEAN THE DATABASE ENTRIES
+  IF(nb0 == 1) THEN
+    DEALLOCATE(dBase); ALLOCATE(dBase(0))
+  ELSE
+    ALLOCATE(tdb(nb0-1)); tdb(1:nb0-1)=dBase(1:nb0-1); CALL MOVE_ALLOC(FROM=tdb, TO=dBase)
+  END IF
+  lerr = dispIsotopes(isot, 'Isotopes parameters read from file', modname)
+
+END FUNCTION readIsotopesFile
+!==============================================================================================================================
+
+!==============================================================================================================================
+!=== IF ISOTOPES (2ND GENERATION TRACERS) ARE DETECTED:                                                                     ===
+!===    * COMPUTE MOST OF THE RELATED QUANTITIES ("isot" COMPONENTS).                                                       ===
+!===    * COMPUTE FEW ISOTOPES-DEDICATED "trac" COMPONENTS                                                                  ===
+!===    * CALL readIsotopesFile TO GET PHYSICAL QUANTITIES (<key>=<val> PAIRS)                                              ===
+!===      NOTE: THIS IS DONE HERE (IN A ROUTINE CALLED BY THE DYNAMIC), BECAUSE THE DYNAMIC NEEDS FEW PHYSICAL PARAMETERS.  ===
+!==============================================================================================================================
+SUBROUTINE initIsotopes(trac, isot)
+  TYPE(trac_type), ALLOCATABLE, TARGET, INTENT(INOUT) :: trac(:)
+  TYPE(isot_type), ALLOCATABLE, TARGET, INTENT(INOUT) :: isot(:)
+  CHARACTER(LEN=maxlen), ALLOCATABLE :: p(:), str(:)                 !--- Temporary storage
+  CHARACTER(LEN=maxlen) :: iname
+  CHARACTER(LEN=1)   :: ph                                           !--- Phase
+  INTEGER :: nbIso, ic, ip, iq, it, iz
+  LOGICAL, ALLOCATABLE :: ll(:)                                      !--- Mask
+  TYPE(trac_type), POINTER   ::  t(:), t1
+  TYPE(isot_type), POINTER   ::  s
+
+  t => trac
+
+  p = PACK(delPhase(t%parent), MASK = t%type=='tracer' .AND. t%iGeneration==2) !--- Parents of 2nd generation isotopes
+  CALL strReduce(p, nbIso)
+  ALLOCATE(isot(nbIso))
+
+  IF(nbIso==0) RETURN                                                !=== NO ISOTOPES: FINISHED
+
+  !--- ISOTOPES RELATED VARIABLES ; NULL OR EMPTY IF NO ISOTOPES
+  isot(:)%parent = p
+  DO ic = 1, SIZE(p)                                                 !--- Loop on isotopes classes
+    s => isot(ic)
+    iname = s%parent                                                 !--- Current isotopes class name (parent tracer name)
+
+    !=== Isotopes childs of tracer "iname": mask, names, number (same for each phase of "iname")
+    ll = t(:)%type=='tracer' .AND. delPhase(t(:)%parent) == iname .AND. t(:)%phase == 'g'
+    str = PACK(delPhase(t(:)%name), MASK = ll)                       !--- Effectively found isotopes of "iname"
+    s%niso = SIZE(str)                                               !--- Number of "effectively found isotopes of "iname"
+    ALLOCATE(s%keys(s%niso))
+    FORALL(it = 1:s%niso) s%keys(it)%name = str(it)
+
+    !=== Geographic tagging tracers descending on tracer "iname": mask, names, number
+    ll = t(:)%type=='tag'    .AND. delPhase(t(:)%gen0Name) == iname .AND. t(:)%iGeneration == 3
+    s%zone = PACK(strTail(t(:)%name,'_',lFirst=.TRUE.), MASK = ll)   !--- Tagging zones names  for isotopes category "iname"
+    CALL strReduce(s%zone)
+    s%nzon = SIZE(s%zone)                                            !--- Tagging zones number for isotopes category "iname"
+
+    !=== Geographic tracers of the isotopes childs of tracer "iname" (same for each phase of "iname")
+    !    NOTE: One might like to create a similar variable for 2nd generation tagging tracers (tagging the gen1 tracers)
+    str = PACK(delPhase(t(:)%name), MASK=ll)
+    CALL strReduce(str)
+    s%nitr = s%niso + SIZE(str)                                      !--- Number of isotopes + their geographic tracers [ntraciso]
+    ALLOCATE(s%trac(s%nitr))
+    FORALL(it = 1:s%niso) s%trac(it) = s%keys(it)%name
+    FORALL(it = s%niso+1:s%nitr) s%trac(it) = str(it-s%niso)
+
+    !=== Phases for tracer "iname"
+    s%phase = ''
+    DO ip = 1, nphases; ph = known_phases(ip:ip); IF(strIdx(t%name,addPhase(iname, ph)) /= 0) s%phase = TRIM(s%phase)//ph; END DO
+    s%npha = LEN_TRIM(s%phase)                                        !--- Equal to "nqo" for water
+
+    !=== Tables giving the index in a table of effectively found items for each dynamical tracer (1<=iq<=nqtot)
+    DO iq = 1, SIZE(t)
+      t1 => trac(iq)
+      IF(delPhase(t1%gen0Name) /= iname) CYCLE                       !--- Only deal with tracers descending on "iname"
+      t1%iso_iGroup = ic                                             !--- Isotopes family       idx in list "isotopes(:)%parent"
+      t1%iso_iName  = strIdx(s%trac, delPhase(strHead(t1%name,'_'))) !--- Current isotope       idx in effective isotopes list
+      t1%iso_iZone  = strIdx(s%zone,          strTail(t1%name,'_') ) !--- Current isotope zone  idx in effective zones    list
+      t1%iso_iPhase =  INDEX(s%phase,TRIM(t1%phase))                 !--- Current isotope phase idx in effective phases   list
+      IF(t1%iGeneration /= 3) t1%iso_iZone = 0                       !--- Skip possible generation 2 tagging tracers
+    END DO
+
+    !=== Table used to get iq (index in dyn array, size nqtot) from the isotope and phase indexes ; the full isotopes list
+    !    (including tagging tracers) is sorted this way:  iso1, iso2, ..., iso1_zone1, iso2_zone1, ..., iso1_zoneN, iso2_zoneN
+    s%iTraPha = RESHAPE( [( (strIdx(t(:)%name,  addPhase(s%trac(it),s%phase(ip:ip))),    it=1, s%nitr), ip=1, s%npha)], &
+                         [s%nitr, s%npha] )
+
+    !=== Table used to get ix (index in tagging tracers isotopes list, size nitr) from the zone and isotope indexes
+    s%iZonIso = RESHAPE( [( (strIdx(s%trac(:), TRIM(s%trac(it))//'_'//TRIM(s%zone(iz))), iz=1, s%nzon), it=1, s%niso)], &
+                         [s%nzon, s%niso] )
+  END DO
+
+  !=== READ PHYSICAL PARAMETERS FROM "isotopes_params.def" FILE
+  !    DONE HERE, AND NOT ONLY IN "infotrac_phy", BECAUSE SOME PHYSICAL PARAMS ARE NEEDED FOR RESTARTS (tnat AND alpha_ideal)
+  IF(readIsotopesFile('isotopes_params.def',isot)) CALL abort_gcm(modname,'Problem when reading isotopes parameters',1)
+
+END SUBROUTINE initIsotopes
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+LOGICAL FUNCTION dispIsotopes(ides, message, modname) RESULT(lerr)
+  TYPE(isot_type),  INTENT(IN) :: ides(:)                            !--- Isotopes descriptor vector
+  CHARACTER(LEN=*), INTENT(IN) :: message                            !--- Message to display
+  CHARACTER(LEN=*), INTENT(IN) :: modname                            !--- Calling subroutine name
+  INTEGER :: ik, nk, ip, it, nt
+  CHARACTER(LEN=maxlen) :: prf
+  CHARACTER(LEN=maxlen), ALLOCATABLE :: ttl(:), val(:,:)
+  CALL msg(TRIM(message)//':', modname)
+  DO ip = 1, SIZE(ides)                                              !--- Loop on parents tracers
+    nk = SIZE(ides(ip)%keys(1)%key)                                  !--- Same keys for each isotope
+    nt = SIZE(ides(ip)%keys)                                         !--- Number of isotopes
+    prf = 'i'//REPEAT('s',nk+1)                                      !--- Profile for table printing
+    ALLOCATE(ttl(nk+2), val(nt,nk+1))
+    ttl(1:2) = ['iq  ','name']; ttl(3:nk+2) = ides(ip)%keys(1)%key(:)!--- Titles line with keys names
+    val(:,1) = ides(ip)%keys(:)%name                                 !--- Values table 1st column: isotopes names  
+    DO ik = 1, nk
+      DO it = 1, nt
+        val(it,ik+1) = ides(ip)%keys(it)%val(ik)                     !--- Other columns: keys values
+      END DO
+    END DO
+    IF(test(fmsg('Problem with the table content', modname, dispTable(prf, ttl, val, cat([(it,it=1,nt)]), rFmt='(EN8.4)')), &
+       lerr)) RETURN
+    DEALLOCATE(ttl, val)
+  END DO        
+END FUNCTION dispIsotopes
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+SUBROUTINE addKey_1(key, val, ky, lOverWrite)
+!------------------------------------------------------------------------------------------------------------------------------
+! Purpose: Add the <key>=<val> pair in the "ky" keys descriptor.
+!------------------------------------------------------------------------------------------------------------------------------
+  CHARACTER(LEN=*),  INTENT(IN)    :: key, val
+  TYPE(keys_type),   INTENT(INOUT) :: ky
+  LOGICAL, OPTIONAL, INTENT(IN)    :: lOverWrite
+  CHARACTER(LEN=maxlen), ALLOCATABLE :: k(:), v(:)
+  INTEGER :: iky, nky
+  LOGICAL :: lo
+!------------------------------------------------------------------------------------------------------------------------------
+  lo=.FALSE.; IF(PRESENT(lOverWrite)) lo=lOverWrite
+  iky = strIdx(ky%key,key)
+  IF(iky == 0) THEN
+    nky = SIZE(ky%key)
+    IF(nky == 0) THEN; ky%key = [key]; ky%val = [val]; ELSE; ky%key = [ky%key, key]; ky%val = [ky%val, val]; END IF
+  ELSE IF(lo) THEN                                                   !--- Overwriting
+    ky%key(iky) = key; ky%val(iky) = val
+  END IF
+END SUBROUTINE addKey_1
+!==============================================================================================================================
+SUBROUTINE addKey_m(key, val, ky, lOverWrite)
+!------------------------------------------------------------------------------------------------------------------------------
+! Purpose: Add the <key>=<val> pair in all the components of the "ky" keys descriptor.
+!------------------------------------------------------------------------------------------------------------------------------
+  CHARACTER(LEN=*),  INTENT(IN)    :: key, val
+  TYPE(keys_type),   INTENT(INOUT) :: ky(:)
+  LOGICAL, OPTIONAL, INTENT(IN)    :: lOverWrite
+  INTEGER :: itr
+  LOGICAL :: lo
+!------------------------------------------------------------------------------------------------------------------------------
+  lo=.FALSE.; IF(PRESENT(lOverWrite)) lo=lOverWrite
+  DO itr = 1, SIZE(ky); CALL addKey_1(key, val, ky(itr), lo); END DO
+END SUBROUTINE addKey_m
+!==============================================================================================================================
+SUBROUTINE addKeysFromDef(t, tr0)
+!------------------------------------------------------------------------------------------------------------------------------
+! Purpose: The values of the keys of the tracer named "tr0" are overwritten by the values found in the *.def files, if any.
+!------------------------------------------------------------------------------------------------------------------------------
+  TYPE(trac_type), ALLOCATABLE, INTENT(INOUT) :: t(:)
+  CHARACTER(LEN=*),             INTENT(IN)    :: tr0
+  CHARACTER(LEN=maxlen) :: val
+  INTEGER               :: ik, jd
+  jd = strIdx(t%name, tr0)
+  IF(jd == 0) RETURN
+  DO ik = 1, SIZE(t(jd)%keys%key)
+    CALL get_in(t(jd)%keys%key(ik), val, 'zzzz')
+    IF(val /= 'zzzz') CALL addKey_1(t(jd)%keys%key(ik), val, t(jd)%keys, .TRUE.)
+  END DO
+END SUBROUTINE addKeysFromDef
+!==============================================================================================================================
+SUBROUTINE delKey_1(itr, keyn, ky)
+!------------------------------------------------------------------------------------------------------------------------------
+! Purpose: Internal routine.
+!   Remove <key>=<val> pairs in the "itr"th component of the "ky" keys descriptor.
+!------------------------------------------------------------------------------------------------------------------------------
+  INTEGER,          INTENT(IN)    :: itr
+  CHARACTER(LEN=*), INTENT(IN)    :: keyn(:)
+  TYPE(trac_type),  INTENT(INOUT) :: ky(:)
+  CHARACTER(LEN=maxlen), ALLOCATABLE :: k(:), v(:)
+  LOGICAL,               ALLOCATABLE :: ll(:)
+  INTEGER :: iky
+!------------------------------------------------------------------------------------------------------------------------------
+  IF(itr<=0 .OR. itr>SIZE(ky, DIM=1)) RETURN                          !--- Index is out of range
+  ll = [( ALL(keyn/=ky(itr)%keys%key(iky)), iky=1, SIZE(ky(itr)%keys%key) )]
+  k = PACK(ky(itr)%keys%key, MASK=ll); CALL MOVE_ALLOC(FROM=k, TO=ky(itr)%keys%key)
+  v = PACK(ky(itr)%keys%val, MASK=ll); CALL MOVE_ALLOC(FROM=v, TO=ky(itr)%keys%val)
+END SUBROUTINE delKey_1
+!==============================================================================================================================
+SUBROUTINE delKey(keyn, ky)
+!------------------------------------------------------------------------------------------------------------------------------
+! Purpose: Internal routine.
+!   Remove <key>=<val> pairs in all the components of the "t" tracers descriptor.
+!------------------------------------------------------------------------------------------------------------------------------
+  CHARACTER(LEN=*), INTENT(IN)    :: keyn(:)
+  TYPE(trac_type),  INTENT(INOUT) :: ky(:)
+  INTEGER :: iky
+!------------------------------------------------------------------------------------------------------------------------------
+  DO iky = 1, SIZE(ky); CALL delKey_1(iky, keyn, ky); END DO
+END SUBROUTINE delKey
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+!=== PUBLIC ROUTINES: GET A KEY FROM A <key>=<val> LIST ; VECTORS, TRACER AND DATABASE VERSIONS ===============================
+!=== BEWARE !!! IF THE "ky" ARGUMENT IS NOT PRESENT, THEN THE VARIABLES "tracers" AND "isotopes" ARE USED. ====================
+!===     THEY ARE LOCAL TO THIS MODULE, SO MUST MUST BE INITIALIZED FIRST USING the "getKey_init" ROUTINE  ====================
+!==============================================================================================================================
+SUBROUTINE getKey_init(tracers_, isotopes_)
+  TYPE(trac_type), OPTIONAL, INTENT(IN) ::  tracers_(:)
+  TYPE(isot_type), OPTIONAL, INTENT(IN) :: isotopes_(:)
+  IF(PRESENT( tracers_))  tracers =  tracers_
+  IF(PRESENT(isotopes_)) isotopes = isotopes_
+END SUBROUTINE getKey_init
+!==============================================================================================================================
+CHARACTER(LEN=maxlen) FUNCTION fgetKey(itr, keyn, ky, def_val) RESULT(out)
+!------------------------------------------------------------------------------------------------------------------------------
+! Purpose: Internal function ; get a key value in string format (this is the returned argument).
+!------------------------------------------------------------------------------------------------------------------------------
+  INTEGER,                    INTENT(IN) :: itr
+  CHARACTER(LEN=*),           INTENT(IN) :: keyn
+  TYPE(keys_type),            INTENT(IN) :: ky(:)
+  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: def_val
+!------------------------------------------------------------------------------------------------------------------------------
+  INTEGER :: ik
+  ik = 0; IF(itr>0 .AND. itr<=SIZE(ky)) ik = strIdx(ky(itr)%key(:), keyn)
+  out = '';              IF(ik /= 0) out = ky(itr)%val(ik)           !--- Key was found
+  IF(PRESENT(def_val) .AND. ik == 0) out = def_val                   !--- Default value from arguments
+END FUNCTION fgetKey
+!==============================================================================================================================
+LOGICAL FUNCTION getKeyByName_s1(keyn, val, tname, ky) RESULT(lerr)
+  !--- Purpose: Get the value of the key named "keyn" for the tracer named "tnam".
+  !     * "ky" unspecified: try in "tracers" for "tnam" with phase and tagging suffixes, then in "isotopes" without.
+  !     * "ky"   specified: try in "ky"      for "tnam" with phase and tagging suffixes, then without.
+  !    The returned error code is always .FALSE.: an empty string is returned when the key hasn't been found.
+  CHARACTER(LEN=*),          INTENT(IN)  :: keyn
+  CHARACTER(LEN=maxlen),     INTENT(OUT) :: val
+  CHARACTER(LEN=*),          INTENT(IN)  :: tname
+  TYPE(keys_type), OPTIONAL, INTENT(IN)  :: ky(:)
+  INTEGER :: is
+  lerr = .FALSE.
+  IF(PRESENT(ky)) THEN
+    val = getKeyByName_prv(keyn, tname , ky);    IF(val /= '') RETURN          !--- "ky" and "tnam"
+    val = getKeyByName_prv(keyn, delPhase(strHead(tname,'_')), ky)             !--- "ky" and "tnam" without phase
+  ELSE
+    IF(.NOT.ALLOCATED(tracers))  RETURN
+    val = getKeyByName_prv(keyn, tname, tracers(:)%keys); IF(val /= '') RETURN !--- "tracers" and "tnam"
+    IF(.NOT.ALLOCATED(isotopes)) RETURN
+    IF(SIZE(isotopes) == 0)      RETURN
+    DO is = 1, SIZE(isotopes); IF(strIdx(isotopes(is)%keys(:)%name, delPhase(strHead(tname,'_'))) /= 0) EXIT; END DO
+    IF(is /= 0) val = getKeyByName_prv(keyn, tname, isotopes(is)%keys(:))      !--- "isotopes" and "tnam" without phase
+  END IF
+
+CONTAINS
+
+FUNCTION getKeyByName_prv(keyn, tname, ky) RESULT(val)
+  CHARACTER(LEN=maxlen)         :: val
+  CHARACTER(LEN=*), INTENT(IN)  :: keyn
+  CHARACTER(LEN=*), INTENT(IN)  :: tname
+  TYPE(keys_type),  INTENT(IN)  :: ky(:)
+  INTEGER :: itr, iky
+  val = ''; iky = 0
+  itr = strIdx(ky(:)%name, tname);                 IF(itr==0) RETURN           !--- Get the index of the wanted tracer
+  IF(itr /= 0) iky = strIdx(ky(itr)%key(:), keyn); IF(iky==0) RETURN           !--- Wanted key    index
+  val = ky(itr)%val(iky)
+END FUNCTION getKeyByName_prv
+
+END FUNCTION getKeyByName_s1
+!==============================================================================================================================
+LOGICAL FUNCTION getKeyByName_sm(keyn, val, tnam, ky) RESULT(lerr)
+  CHARACTER(LEN=*),                   INTENT(IN)  :: keyn
+  CHARACTER(LEN=maxlen), ALLOCATABLE, INTENT(OUT) ::  val(:)
+  CHARACTER(LEN=*), TARGET, OPTIONAL, INTENT(IN)  :: tnam(:)
+  TYPE(keys_type),  TARGET, OPTIONAL, INTENT(IN)  ::   ky(:)
+  CHARACTER(LEN=maxlen),    POINTER :: n(:)
+  INTEGER :: iq
+  n => tracers(:)%keys%name; IF(PRESENT(tnam)) n => tnam(:)
+  ALLOCATE(val(SIZE(n)))
+  IF(     PRESENT(ky)) lerr = ANY([(getKeyByName_s1(keyn, val(iq), n(iq), ky), iq=1, SIZE(n))])
+  IF(.NOT.PRESENT(ky)) lerr = ANY([(getKeyByName_s1(keyn, val(iq), n(iq)),     iq=1, SIZE(n))])
+END FUNCTION getKeyByName_sm
+!==============================================================================================================================
+LOGICAL FUNCTION getKeyByName_i1(keyn, val, tnam, ky) RESULT(lerr)
+  CHARACTER(LEN=*),          INTENT(IN)  :: keyn
+  INTEGER,                   INTENT(OUT) :: val
+  CHARACTER(LEN=*),          INTENT(IN)  :: tnam
+  TYPE(keys_type), OPTIONAL, INTENT(IN)  :: ky(:)
+  CHARACTER(LEN=maxlen) :: sval
+  INTEGER :: ierr
+  IF(     PRESENT(ky)) lerr = getKeyByName_s1(keyn, sval, tnam, ky)
+  IF(.NOT.PRESENT(ky)) lerr = getKeyByName_s1(keyn, sval, tnam)
+  IF(test(fmsg('key "'//TRIM(keyn)//'" or tracer "'//TRIM(tnam)//'" is missing',        modname, lerr), lerr)) RETURN
+  READ(sval, *, IOSTAT=ierr) val
+  IF(test(fmsg('key "'//TRIM(keyn)//'" of tracer "'//TRIM(tnam)//'" is not an integer', modname, lerr), lerr)) RETURN
+END FUNCTION getKeyByName_i1
+!==============================================================================================================================
+LOGICAL FUNCTION getKeyByName_im(keyn, val, tnam, ky) RESULT(lerr)
+  CHARACTER(LEN=*),                   INTENT(IN)  :: keyn
+  INTEGER,               ALLOCATABLE, INTENT(OUT) ::  val(:)
+  CHARACTER(LEN=*), TARGET, OPTIONAL, INTENT(IN)  :: tnam(:)
+  TYPE(keys_type),  TARGET, OPTIONAL, INTENT(IN)  ::   ky(:)
+  CHARACTER(LEN=maxlen), POINTER :: n(:)
+  INTEGER :: iq
+  n => tracers(:)%name; IF(PRESENT(tnam)) n => tnam(:)
+  ALLOCATE(val(SIZE(n)))
+  IF(     PRESENT(ky)) lerr = ANY([(getKeyByName_i1(keyn, val(iq), n(iq), ky), iq=1, SIZE(n))])
+  IF(.NOT.PRESENT(ky)) lerr = ANY([(getKeyByName_i1(keyn, val(iq), n(iq)),     iq=1, SIZE(n))])
+END FUNCTION getKeyByName_im
+!==============================================================================================================================
+LOGICAL FUNCTION getKeyByName_r1(keyn, val, tnam, ky) RESULT(lerr)
+  CHARACTER(LEN=*),          INTENT(IN)  :: keyn
+  REAL,                      INTENT(OUT) :: val
+  CHARACTER(LEN=*),          INTENT(IN)  :: tnam
+  TYPE(keys_type), OPTIONAL, INTENT(IN)  :: ky(:)
+  CHARACTER(LEN=maxlen) :: sval
+  INTEGER :: ierr
+  IF(     PRESENT(ky)) lerr = getKeyByName_s1(keyn, sval, tnam, ky)
+  IF(.NOT.PRESENT(ky)) lerr = getKeyByName_s1(keyn, sval, tnam)
+  IF(test(fmsg('key "'//TRIM(keyn)//'" or tracer "'//TRIM(tnam)//'" is missing',    modname, lerr), lerr)) RETURN
+  READ(sval, *, IOSTAT=ierr) val
+  IF(test(fmsg('key "'//TRIM(keyn)//'" of tracer "'//TRIM(tnam)//'" is not a real', modname, lerr), lerr)) RETURN
+END FUNCTION getKeyByName_r1
+!==============================================================================================================================
+LOGICAL FUNCTION getKeyByName_rm(keyn, val, tnam, ky) RESULT(lerr)
+  CHARACTER(LEN=*),                   INTENT(IN)  :: keyn
+  REAL,                  ALLOCATABLE, INTENT(OUT) ::  val(:)
+  CHARACTER(LEN=*), TARGET, OPTIONAL, INTENT(IN)  :: tnam(:)
+  TYPE(keys_type),  TARGET, OPTIONAL, INTENT(IN)  ::   ky(:)
+  CHARACTER(LEN=maxlen), POINTER :: n(:)
+  INTEGER :: iq
+  n => tracers(:)%name;  IF(PRESENT(tnam)) n => tnam(:)
+  ALLOCATE(val(SIZE(n)))
+  IF(     PRESENT(ky)) lerr = ANY([(getKeyByName_r1(keyn, val(iq), n(iq), ky), iq=1, SIZE(n))])
+  IF(.NOT.PRESENT(ky)) lerr = ANY([(getKeyByName_r1(keyn, val(iq), n(iq)),     iq=1, SIZE(n))])
+END FUNCTION getKeyByName_rm
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+!=== REMOVE, IF ANY, OR ADD THE PHASE SUFFIX OF THE TRACER NAMED "s" ==========================================================
+!==============================================================================================================================
+ELEMENTAL CHARACTER(LEN=maxlen) FUNCTION delPhase(s) RESULT(out)
+  CHARACTER(LEN=*), INTENT(IN) :: s
+  INTEGER :: l, i, ix
+  out = s
+  IF(s == '') RETURN                                                           !--- Empty string: nothing to do
+  !--- Index of found phase in "known_phases"
+  ix = MAXLOC( [(i, i=1,nphases)], MASK=[( INDEX(s, phases_sep//known_phases(i:i))/=0, i=1, nphases)], DIM=1 )
+  IF(ix == 0) RETURN                                                           !--- No phase pattern found
+  i = INDEX(s, phases_sep//known_phases(ix:ix))                                !--- Index of <sep><pha> pattern in "str"
+  l = LEN_TRIM(s)
+  IF(i == l-1) THEN                                                            !--- <var><sep><pha>       => return <var>
+    out = s(1:l-2)
+  ELSE IF(s(i+2:i+2) == '_') THEN                                              !--- <var><sep><pha>_<tag> => return <var>_<tag>
+    out = s(1:i-1)//s(i+2:l)
+  END IF
+END FUNCTION delPhase
+!------------------------------------------------------------------------------------------------------------------------------
+CHARACTER(LEN=maxlen) FUNCTION addPhase_1(s,pha) RESULT(out)
+  CHARACTER(LEN=*), INTENT(IN) :: s
+  CHARACTER(LEN=1), INTENT(IN) :: pha
+  INTEGER :: l, i
+  out = s
+  IF(s == '') RETURN                                                           !--- Empty string: nothing to do
+  i = INDEX(s, '_')                                                            !--- /=0 for <var>_<tag> tracers names
+  l = LEN_TRIM(s)
+  IF(i == 0) out =  TRIM(s)//phases_sep//pha                                   !--- <var>       => return <var><sep><pha>
+  IF(i /= 0) out = s(1:i-1)//phases_sep//pha//'_'//s(i+1:l)                    !--- <var>_<tag> => return <var><sep><pha>_<tag>
+END FUNCTION addPhase_1
+!------------------------------------------------------------------------------------------------------------------------------
+FUNCTION addPhase_m(s,pha) RESULT(out)
+  CHARACTER(LEN=*),      INTENT(IN)  :: s(:)
+  CHARACTER(LEN=1),      INTENT(IN)  :: pha
+  CHARACTER(LEN=maxlen), ALLOCATABLE :: out(:)  
+  INTEGER :: k
+  out = [( addPhase_1(s(k), pha), k=1, SIZE(s) )]
+END FUNCTION addPhase_m
+!------------------------------------------------------------------------------------------------------------------------------
+
+
+!==============================================================================================================================
+!=== GET THE NAME(S) OF THE ANCESTOR(S) OF TRACER(S) "tname" AT GENERATION "igen"  IN THE TRACERS DESCRIPTORS LIST "tr" =======
+!==============================================================================================================================
+CHARACTER(LEN=maxlen) FUNCTION ancestor_1(t, tname, igen) RESULT(out)
+  TYPE(trac_type),   INTENT(IN) :: t(:)
+  CHARACTER(LEN=*),  INTENT(IN) :: tname
+  INTEGER, OPTIONAL, INTENT(IN) :: igen
+  INTEGER :: ig, ix
+  ig = 1; IF(PRESENT(igen)) ig = igen
+  ix = idxAncestor_1(t, tname, ig)
+  out = ''; IF(ix /= 0) out = t(ix)%name
+END FUNCTION ancestor_1
+!------------------------------------------------------------------------------------------------------------------------------
+FUNCTION ancestor_m(t, tname, igen) RESULT(out)
+  CHARACTER(LEN=maxlen), ALLOCATABLE     ::   out(:)
+  TYPE(trac_type),            INTENT(IN) ::     t(:)
+  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: tname(:)
+  INTEGER,          OPTIONAL, INTENT(IN) :: igen
+  INTEGER, ALLOCATABLE :: ix(:)
+  INTEGER :: ig
+  ig = 1; IF(PRESENT(igen)) ig = igen
+  IF(     PRESENT(tname)) ix = idxAncestor_m(t, tname,     ig)
+  IF(.NOT.PRESENT(tname)) ix = idxAncestor_m(t, t(:)%name, ig)
+  ALLOCATE(out(SIZE(ix))); out(:) = ''
+  WHERE(ix /= 0) out = t(ix)%name
+END FUNCTION ancestor_m
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+!=== GET THE INDEX(ES) OF THE ANCESTOR(S) OF TRACER(S) "tname" AT GENERATION "igen"  IN THE TRACERS DESCRIPTORS LIST "tr" =====
+!==============================================================================================================================
+INTEGER FUNCTION idxAncestor_1(t, tname, igen) RESULT(out)
+! Return the name of the generation "igen" ancestor of "tname"
+  TYPE(trac_type),   INTENT(IN) :: t(:)
+  CHARACTER(LEN=*),  INTENT(IN) :: tname
+  INTEGER, OPTIONAL, INTENT(IN) :: igen
+  INTEGER :: ig
+  ig = 1; IF(PRESENT(igen)) ig = igen
+  out = strIdx(t(:)%name, tname)
+  IF(out == 0)          RETURN
+  IF(t(out)%iGeneration <= ig) RETURN
+  DO WHILE(t(out)%iGeneration > ig); out = strIdx(t(:)%name, t(out)%parent); END DO
+END FUNCTION idxAncestor_1
+!------------------------------------------------------------------------------------------------------------------------------
+FUNCTION idxAncestor_m(t, tname, igen) RESULT(out)
+  INTEGER,          ALLOCATABLE          ::   out(:)
+  TYPE(trac_type),            INTENT(IN) ::     t(:)
+  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: tname(:)
+  INTEGER,          OPTIONAL, INTENT(IN) :: igen
+  INTEGER :: ig, ix
+  ig = 1; IF(PRESENT(igen)) ig = igen
+  IF(     PRESENT(tname)) out = [(idxAncestor_1(t, tname(ix),  ig), ix=1, SIZE(tname))]
+  IF(.NOT.PRESENT(tname)) out = [(idxAncestor_1(t, t(ix)%name, ig), ix=1, SIZE(t))]
+END FUNCTION idxAncestor_m
+!==============================================================================================================================
+
+
+END MODULE readTracFiles_mod
Index: LMDZ6/trunk/libf/misc/strings_mod.F90
===================================================================
--- LMDZ6/trunk/libf/misc/strings_mod.F90	(revision 4046)
+++ LMDZ6/trunk/libf/misc/strings_mod.F90	(revision 4046)
@@ -0,0 +1,1401 @@
+MODULE strings_mod
+
+  IMPLICIT NONE
+
+  PRIVATE
+  PUBLIC :: maxlen, init_printout, msg, fmsg, get_in, lunout, prt_level
+  PUBLIC :: strLower, strHead, strStack,  strClean,  strIdx,  strCount, strReplace
+  PUBLIC :: strUpper, strTail, strStackm, strReduce, strFind, strParse, cat, find
+  PUBLIC :: dispTable, dispOutliers, dispNameList
+  PUBLIC :: is_numeric, bool2str, int2str, real2str, dble2str
+  PUBLIC :: reduceExpr, str2bool, str2int, str2real, str2dble
+  PUBLIC :: addQuotes, testFile, checkList, removeComment, test
+
+  INTERFACE get_in;     MODULE PROCEDURE getin_s,  getin_i,  getin_r,  getin_l;  END INTERFACE get_in
+  INTERFACE  msg;       MODULE PROCEDURE        msg_1,                    msg_m; END INTERFACE  msg
+  INTERFACE fmsg;       MODULE PROCEDURE       fmsg_1,                   fmsg_m; END INTERFACE fmsg
+  INTERFACE strHead;    MODULE PROCEDURE    strHead_1,                strHead_m; END INTERFACE strHead
+  INTERFACE strTail;    MODULE PROCEDURE    strTail_1,                strTail_m; END INTERFACE strTail
+  INTERFACE strClean;   MODULE PROCEDURE   strClean_1,               strClean_m; END INTERFACE strClean
+  INTERFACE strReduce;  MODULE PROCEDURE  strReduce_1,              strReduce_2; END INTERFACE strReduce
+  INTERFACE strIdx;     MODULE PROCEDURE     strIdx_1,                 strIdx_m; END INTERFACE strIdx
+  INTERFACE strCount;   MODULE PROCEDURE  strCount_m1, strCount_11, strCount_1m; END INTERFACE strCount
+  INTERFACE strParse;   MODULE PROCEDURE   strParse_1,               strParse_m; END INTERFACE strParse
+  INTERFACE strReplace; MODULE PROCEDURE strReplace_1,             strReplace_m; END INTERFACE strReplace
+  INTERFACE cat;        MODULE PROCEDURE   horzcat_s1,  horzcat_i1,  horzcat_r1, &
+!                 horzcat_d1,  horzcat_dm,
+                                           horzcat_sm,  horzcat_im,  horzcat_rm; END INTERFACE cat
+  INTERFACE find;       MODULE PROCEDURE      strFind,    find_int,    find_boo; END INTERFACE find
+  INTERFACE dispOutliers; MODULE PROCEDURE dispOutliers_1, dispOutliers_2; END INTERFACE dispOutliers
+  INTERFACE reduceExpr;   MODULE PROCEDURE   reduceExpr_1,   reduceExpr_m; END INTERFACE reduceExpr
+  INTERFACE addQuotes;    MODULE PROCEDURE    addQuotes_1,    addQuotes_m; END INTERFACE addQuotes
+  INTERFACE testFile;     MODULE PROCEDURE     testFile_1,     testFile_m; END INTERFACE testFile
+
+  INTEGER, PARAMETER :: maxlen    = 256                    !--- Standard maximum length for strings
+  INTEGER,      SAVE :: lunout    = 6                      !--- Printing unit  (default: 6, ie. on screen)
+  INTEGER,      SAVE :: prt_level = 1                      !--- Printing level (default: 1, ie. print all)
+
+CONTAINS
+
+LOGICAL FUNCTION test(lcond, lout) RESULT(lerr)
+  LOGICAL, INTENT(IN)  :: lcond
+  LOGICAL, INTENT(OUT) :: lout
+  lerr = lcond; lout = lcond
+END FUNCTION test
+
+!==============================================================================================================================
+SUBROUTINE init_printout(lunout_, prt_level_)
+  INTEGER, INTENT(IN) :: lunout_, prt_level_
+  lunout = lunout_
+END SUBROUTINE init_printout
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+!=== Same as getin ; additional last argument: the default value.
+!==============================================================================================================================
+SUBROUTINE getin_s(nam, val, def)
+USE ioipsl_getincom, ONLY: getin
+  CHARACTER(LEN=*), INTENT(IN)    :: nam
+  CHARACTER(LEN=*), INTENT(INOUT) :: val
+  CHARACTER(LEN=*), INTENT(IN)    :: def
+  val = def; CALL getin(nam, val)
+  IF(val/=def) WRITE(lunout,*)TRIM(nam)//' = '//TRIM(val)
+END SUBROUTINE getin_s
+!==============================================================================================================================
+SUBROUTINE getin_i(nam, val, def)
+USE ioipsl_getincom, ONLY: getin
+  CHARACTER(LEN=*), INTENT(IN)    :: nam
+  INTEGER,          INTENT(INOUT) :: val
+  INTEGER,          INTENT(IN)    :: def
+  val = def; CALL getin(nam, val)
+  IF(val/=def) WRITE(lunout,*)TRIM(nam)//' = '//TRIM(int2str(val))
+END SUBROUTINE getin_i
+!==============================================================================================================================
+SUBROUTINE getin_r(nam, val, def)
+USE ioipsl_getincom, ONLY: getin
+  CHARACTER(LEN=*), INTENT(IN)    :: nam
+  REAL,             INTENT(INOUT) :: val
+  REAL,             INTENT(IN)    :: def
+  val = def; CALL getin(nam, val)
+  IF(val/=def) WRITE(lunout,*)TRIM(nam)//' = '//TRIM(real2str(val))
+END SUBROUTINE getin_r
+!==============================================================================================================================
+SUBROUTINE getin_l(nam, val, def)
+USE ioipsl_getincom, ONLY: getin
+  CHARACTER(LEN=*), INTENT(IN)    :: nam
+  LOGICAL,          INTENT(INOUT) :: val
+  LOGICAL,          INTENT(IN)    :: def
+  val = def; CALL getin(nam, val)
+  IF(val/=def) WRITE(lunout,*)TRIM(nam)//' = '//TRIM(bool2str(val))
+END SUBROUTINE getin_l
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+!=== Display one or several messages, one each line, starting with the current routine name "modname".
+!==============================================================================================================================
+SUBROUTINE msg_1(str, modname, ll, unit)
+  !--- Display a simple message "str". Optional parameters:
+  !    * "modname": module name, displayed in front of the message (with ": " separator) if present.
+  !    * "ll":      message trigger ; message is displayed only if ll==.TRUE.
+  !    * "unit":    write unit (by default: "lunout")
+  CHARACTER(LEN=*),           INTENT(IN) :: str
+  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: modname
+  LOGICAL,          OPTIONAL, INTENT(IN) :: ll
+  INTEGER,          OPTIONAL, INTENT(IN) :: unit
+  INTEGER :: unt
+  IF(PRESENT(ll)) THEN; IF(ll) RETURN; END IF
+  unt = lunout; IF(PRESENT(unit)) unt = unit
+  IF(PRESENT(modname)) THEN
+    WRITE(unt,'(a)') TRIM(modname)//': '//str              !--- Routine name provided
+  ELSE
+    WRITE(unt,'(a)') str                                   !--- Simple message
+  END IF
+END SUBROUTINE msg_1
+!==============================================================================================================================
+SUBROUTINE msg_m(str, modname, ll, unit, nmax)
+  !--- Same as msg_1 with multiple strings that are stacked (separator: coma) on up to "nmax" full lines.
+  CHARACTER(LEN=*),           INTENT(IN) :: str(:)
+  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: modname
+  LOGICAL,          OPTIONAL, INTENT(IN) :: ll
+  INTEGER,          OPTIONAL, INTENT(IN) :: unit
+  INTEGER,          OPTIONAL, INTENT(IN) :: nmax
+  CHARACTER(LEN=maxlen), ALLOCATABLE :: s(:)
+  INTEGER :: unt, nmx, k
+  LOGICAL :: lerr
+  lerr = .TRUE.; IF(PRESENT(ll))  lerr = ll
+  unt = lunout ; IF(PRESENT(unit)) unt = unit
+  nmx = 128;     IF(PRESENT(nmax)) nmx = nmax
+  s = strStackm(str, ', ', nmx)
+  IF(PRESENT(modname)) THEN
+    DO k=1,SIZE(s); CALL msg_1(s(k), modname, lerr, unt); END DO
+  ELSE
+    DO k=1,SIZE(s); CALL msg_1(s(k), ll=lerr, unit=unt);  END DO
+  END IF
+END SUBROUTINE msg_m
+!==============================================================================================================================
+LOGICAL FUNCTION fmsg_1(str, modname, ll, unit) RESULT(lerr)
+  CHARACTER(LEN=*),           INTENT(IN) :: str
+  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: modname
+  LOGICAL,          OPTIONAL, INTENT(IN) :: ll
+  INTEGER,          OPTIONAL, INTENT(IN) :: unit
+  INTEGER :: unt
+  lerr = .TRUE.; IF(PRESENT(ll))  lerr = ll
+  unt = lunout ; IF(PRESENT(unit)) unt = unit
+  IF(PRESENT(modname)) THEN
+    CALL msg_1(str, modname, lerr, unt)
+  ELSE
+    CALL msg_1(str, ll=lerr, unit=unt)
+  END IF
+END FUNCTION fmsg_1
+!==============================================================================================================================
+LOGICAL FUNCTION fmsg_m(str, modname, ll, unit, nmax) RESULT(lerr)
+  CHARACTER(LEN=*),           INTENT(IN)  :: str(:)
+  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: modname
+  LOGICAL,          OPTIONAL, INTENT(IN) :: ll
+  INTEGER,          OPTIONAL, INTENT(IN) :: unit
+  INTEGER,          OPTIONAL, INTENT(IN)  :: nmax
+  INTEGER :: unt, nmx
+  lerr = .TRUE.; IF(PRESENT(ll))  lerr = ll
+  unt = lunout ; IF(PRESENT(unit)) unt = unit
+  nmx = 128;     IF(PRESENT(nmax)) nmx = nmax
+  IF(PRESENT(modname)) THEN
+    CALL msg_m(str, modname, lerr, unt, nmx)
+  ELSE
+    CALL msg_m(str, ll=lerr, unit=unt, nmax=nmx)
+  END IF
+END FUNCTION fmsg_m
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+!=== Lower/upper case conversion function. ====================================================================================
+!==============================================================================================================================
+ELEMENTAL CHARACTER(LEN=maxlen) FUNCTION strLower(str) RESULT(out)
+  CHARACTER(LEN=*), INTENT(IN) :: str
+  INTEGER :: k
+  out = str
+  DO k=1,LEN_TRIM(str)
+    IF(str(k:k)>='A'.OR.str(k:k)<='Z') out(k:k)=ACHAR(IACHAR(str(k:k))+32)
+  END DO
+END FUNCTION strLower
+!==============================================================================================================================
+ELEMENTAL CHARACTER(LEN=maxlen) FUNCTION strUpper(str) RESULT(out)
+  CHARACTER(LEN=*), INTENT(IN) :: str
+  INTEGER :: k
+  out = str
+  DO k=1,LEN_TRIM(str)
+    IF(str(k:k)>='a'.OR.str(k:k)<='z') out(k:k)=ACHAR(IACHAR(str(k:k))-32)
+  END DO
+END FUNCTION strUpper
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+!=== Extract the substring in front of the last (first if lFirst==TRUE) occurrence of separator "sep" in "str" ================
+!=== Examples for str='a_b_c' and sep='_' and the bash command with the same effect:                           ================
+!===    * strHead(..,.FALSE.) = 'a'           ${str%%$sep*}                                                    ================
+!===    * strHead(..,.TRUE.)  = 'a_b'         ${str%$sep*}                                                     ================
+!==============================================================================================================================
+CHARACTER(LEN=maxlen) FUNCTION strHead_1(str,sep,lFirst) RESULT(out)
+  CHARACTER(LEN=*),           INTENT(IN) :: str
+  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: sep
+  LOGICAL,          OPTIONAL, INTENT(IN) :: lFirst
+  LOGICAL :: lf
+  lf = .FALSE.; IF(PRESENT(lFirst)) lf=lFirst
+  IF(PRESENT(sep)) THEN
+    out = str(1:INDEX(str,sep,.NOT.lf)-1)
+  ELSE
+    out = str(1:INDEX(str,'/',.NOT.lf)-1)
+  END IF
+  IF(out == '') out = str
+END FUNCTION strHead_1
+!==============================================================================================================================
+FUNCTION strHead_m(str,sep,lFirst) RESULT(out)
+  CHARACTER(LEN=maxlen),     ALLOCATABLE :: out(:)
+  CHARACTER(LEN=*),           INTENT(IN) :: str(:)
+  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: sep
+  LOGICAL,          OPTIONAL, INTENT(IN) :: lFirst
+  LOGICAL :: lf
+  INTEGER :: k
+  lf = .FALSE.; IF(PRESENT(lFirst)) lf=lFirst
+  IF(PRESENT(sep)) THEN
+    out = [(strHead_1(str(k),sep,.NOT.lf),    k=1, SIZE(str))]
+  ELSE
+    out = [(strHead_1(str(k),lFirst=.NOT.lf), k=1, SIZE(str))]
+  END IF
+END FUNCTION strHead_m
+!==============================================================================================================================
+!=== Extract the substring following the last (first if lFirst==TRUE) occurrence of separator "sep" in "str"   ================
+!=== Examples for str='a_b_c' and sep='_' and the bash command with the same effect:                           ================
+!===    * strHead(..,.FALSE.) = 'b_c'         ${str#*$sep}                                                     ================
+!===    * strHead(..,.TRUE.)  = 'c'           ${str##*$sep}                                                    ================
+!==============================================================================================================================
+CHARACTER(LEN=maxlen) FUNCTION strTail_1(str,sep,lFirst) RESULT(out)
+  CHARACTER(LEN=*),           INTENT(IN) :: str
+  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: sep
+  LOGICAL,          OPTIONAL, INTENT(IN) :: lFirst
+  LOGICAL :: lf
+  lf = .FALSE.; IF(PRESENT(lFirst)) lf=lFirst
+  IF(PRESENT(sep)) THEN
+    out = str(INDEX(str,sep,.NOT.lf)+LEN(sep):LEN_TRIM(str))
+  ELSE
+    out = str(INDEX(str,'/',.NOT.lf)+1:LEN_TRIM(str))
+  END IF
+  IF(out == '') out = str
+END FUNCTION strTail_1
+!==============================================================================================================================
+FUNCTION strTail_m(str,sep,lFirst) RESULT(out)
+  CHARACTER(LEN=maxlen),     ALLOCATABLE :: out(:)
+  CHARACTER(LEN=*),           INTENT(IN) :: str(:)
+  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: sep
+  LOGICAL,          OPTIONAL, INTENT(IN) :: lFirst
+  LOGICAL :: lf
+  INTEGER :: k
+  lf = .FALSE.; IF(PRESENT(lFirst)) lf=lFirst
+  IF(PRESENT(sep)) THEN
+    out = [(strTail_1(str(k),sep,.NOT.lf),    k=1, SIZE(str))]
+  ELSE
+    out = [(strTail_1(str(k),lFirst=.NOT.lf), k=1, SIZE(str))]
+  END IF
+END FUNCTION strTail_m
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+!=== Concatenates the strings "str(:)" with separator "sep" into a single string using a separator (',' by default). ==========
+!==============================================================================================================================
+FUNCTION strStack(str, sep) RESULT(out)
+  CHARACTER(LEN=:),          ALLOCATABLE :: out
+  CHARACTER(LEN=*),           INTENT(IN) :: str(:)
+  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: sep
+  CHARACTER(LEN=:), ALLOCATABLE :: s
+  INTEGER :: is
+  IF(SIZE(str) == 0) THEN; out = ''; RETURN; END IF
+  ALLOCATE(s, SOURCE=', '); IF(PRESENT(sep)) s=sep
+  out = str(1); DO is=2,SIZE(str, DIM=1); out = TRIM(out)//s//TRIM(str(is)); END DO
+END FUNCTION strStack
+!==============================================================================================================================
+!=== Concatenate the strings "str(:)" with separator "sep" into one or several lines of "nmax" characters max (for display) ===
+!==============================================================================================================================
+FUNCTION strStackm(str, sep, nmax) RESULT(out)
+  CHARACTER(LEN=maxlen),     ALLOCATABLE :: out(:)
+  CHARACTER(LEN=*),           INTENT(IN) :: str(:)
+  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: sep
+  INTEGER,          OPTIONAL, INTENT(IN) :: nmax
+  CHARACTER(LEN=maxlen), ALLOCATABLE :: t(:)
+  CHARACTER(LEN=maxlen) :: sp
+  INTEGER :: is, ns, no, mx, n
+  IF(SIZE(str) == 0) THEN; out = ['']; RETURN; END IF
+  sp =', '; IF(PRESENT(sep )) sp = sep
+  ns = 2  ; IF(PRESENT(sep )) ns = LEN(sep)
+  mx = 256; IF(PRESENT(nmax)) mx = nmax
+  no = 1; out = ['']
+  DO is = 1, SIZE(str)
+    n = LEN_TRIM(str(is)); IF(out(no)/='') n = n+ns+LEN_TRIM(out(no))          !--- Line length after "str(is)" inclusion
+    IF(out(no) == '') THEN
+      out(no) = str(is)                                                        !--- Empty new line: set to "str(is)"
+    ELSE IF(n <= mx) THEN
+      out(no) = TRIM(out(no))//sp(1:ns)//TRIM(str(is))                         !--- Append "str(is)" to the current line
+    ELSE
+      ALLOCATE(t(no+1)); t(1:no) = out; no=no+1; t(no) = str(is)               !--- Full line: "str(si)" put in next line
+      CALL MOVE_ALLOC(FROM=t, TO=out)
+    END IF
+  END DO
+END FUNCTION strStackm
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+!=== String cleaning: replace tabulation by spaces, remove NULL characters and comments. ======================================
+!==============================================================================================================================
+SUBROUTINE strClean_1(str)
+  CHARACTER(LEN=*), INTENT(INOUT) :: str
+  INTEGER :: k, n, m
+  n = LEN(str)
+  DO k = n, 1, -1
+    m = IACHAR(str(k:k))
+    IF(m==9) str(k:k) = ' '                           !--- Replace the tabulations with spaces
+    IF(m==0) str(k:n) = str(k+1:n)//' '               !--- Remove the NULL characters
+  END DO
+  m = INDEX(str,'!')-1; IF(m==-1) m = LEN_TRIM(str)   !--- Remove end of line comment
+  str = ADJUSTL(str(1:m))
+END SUBROUTINE strClean_1
+!==============================================================================================================================
+SUBROUTINE strClean_m(str)
+  CHARACTER(LEN=*), INTENT(INOUT) :: str(:)
+  INTEGER :: k
+  DO k = 1, SIZE(str); CALL strClean_1(str(k)); END DO
+END SUBROUTINE strClean_m
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+!=== strReduce_1(str1)     : Remove duplicated elements of str1.    ===========================================================
+!=== strReduce_2(str1,str2): Append str1 with new elements of str2. ===========================================================
+!==============================================================================================================================
+SUBROUTINE strReduce_1(str1, nb)
+  CHARACTER(LEN=*), ALLOCATABLE, INTENT(INOUT) :: str1(:)
+  INTEGER,          OPTIONAL,    INTENT(OUT)   :: nb
+  CHARACTER(LEN=maxlen), ALLOCATABLE :: s1(:)
+  INTEGER :: k, n, n1
+  IF(PRESENT(nb)) nb = 0
+  CALL MOVE_ALLOC(FROM = str1, TO = s1); CALL strClean(s1)
+  n1 = SIZE(s1, DIM=1)                                     !--- Total nb of  elements in "s1"
+  n  = COUNT( [( ALL(s1(1:k-1)/=s1(k)), k=1, n1 )] )       !--- Nb of unique elements in "s1"
+  ALLOCATE(str1(n)); IF(n==0) RETURN; str1(1) = s1(1)
+  n=1; DO k=2,n1; IF(ANY(s1(1:k-1)==s1(k))) CYCLE; n=n+1; str1(n)=s1(k); END DO
+  IF(PRESENT(nb)) nb = n
+END SUBROUTINE strReduce_1
+!==============================================================================================================================
+SUBROUTINE strReduce_2(str1, str2)
+  CHARACTER(LEN=*),   ALLOCATABLE, INTENT(INOUT) :: str1(:)
+  CHARACTER(LEN=*),                INTENT(IN)    :: str2(:)
+  CHARACTER(LEN=maxlen), ALLOCATABLE :: s1(:), s2(:)
+  INTEGER :: k
+  IF(SIZE(str2)==0) RETURN
+  s2 = str2; CALL strClean(s2)
+  IF(.NOT.ALLOCATED(s2)) RETURN
+  IF(SIZE(s2) == 0) THEN; DEALLOCATE(s2); RETURN; END IF
+  IF(.NOT.ALLOCATED(str1)) THEN
+    str1 = s2
+  ELSE IF(SIZE(str1)==0) THEN
+    str1 = s2
+  ELSE
+    s1 = str1; CALL strClean(s1)
+    str1 = [s1, PACK(s2, MASK= [( ALL(s1(:) /= s2(k)), k=1, SIZE(s2) )] ) ]
+  END IF
+END SUBROUTINE strReduce_2
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+!=== GET THE INDEX OF THE FIRST APPEARANCE IN THE STRING VECTOR "str(:)" OF THE STRING(s) "s" =================================
+!==============================================================================================================================
+INTEGER FUNCTION strIdx_1(str, s) RESULT(out)
+  CHARACTER(LEN=*), INTENT(IN) :: str(:), s
+  DO out = 1, SIZE(str); IF(str(out) == s) EXIT; END DO
+  IF(out == 1+SIZE(str) .OR. SIZE(str)==0) out = 0
+END FUNCTION strIdx_1
+!==============================================================================================================================
+FUNCTION strIdx_m(str, s, n) RESULT(out)
+  CHARACTER(LEN=*),  INTENT(IN)  :: str(:), s(:)
+  INTEGER, OPTIONAL, INTENT(OUT) :: n
+  INTEGER,           ALLOCATABLE :: out(:)
+  INTEGER :: k
+  out = [(strIdx_1(str(:), s(k)), k=1, SIZE(s))]
+  IF(PRESENT(n)) n = COUNT(out(:)/=0)
+END FUNCTION strIdx_m
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+!=== GET THE INDEX LIST OF THE ELEMENTS OF "str(:)" EQUAL TO "s" AND OPTIONALY, ITS LENGTH "n" ================================
+!==============================================================================================================================
+FUNCTION strFind(str, s, n) RESULT(out)
+  CHARACTER(LEN=*),  INTENT(IN)  :: str(:), s
+  INTEGER, OPTIONAL, INTENT(OUT) :: n
+  INTEGER,           ALLOCATABLE :: out(:)
+  INTEGER :: k
+  out = PACK( [(k, k=1, SIZE(str(:), DIM=1))], MASK = str(:) == s )
+  IF(PRESENT(n)) n = SIZE(out(:), DIM=1)
+END FUNCTION strFind
+!==============================================================================================================================
+FUNCTION find_int(i,j,n) RESULT(out)
+  INTEGER,           INTENT(IN)  :: i(:), j
+  INTEGER, OPTIONAL, INTENT(OUT) :: n
+  INTEGER,           ALLOCATABLE :: out(:)
+  INTEGER :: k
+  out = PACK( [(k, k=1, SIZE(i(:), DIM=1))], MASK = i(:) == j )
+  IF(PRESENT(n)) n = SIZE(out(:), DIM=1)
+END FUNCTION find_int
+!==============================================================================================================================
+FUNCTION find_boo(l,n) RESULT(out)
+  LOGICAL,           INTENT(IN)  :: l(:)
+  INTEGER, OPTIONAL, INTENT(OUT) :: n
+  INTEGER,           ALLOCATABLE :: out(:)
+  INTEGER :: k
+  out = PACK( [(k, k=1, SIZE(l(:), DIM=1))], MASK = l(:) )
+  IF(PRESENT(n)) n = SIZE(out(:), DIM=1)
+END FUNCTION find_boo
+!==============================================================================================================================
+
+
+
+!==============================================================================================================================
+!=== GET 1ST APPEARANCE INDEX OF EACH ELEMENT OF "t(:)" IN "s(:)" (UNFOUND: INDEX=0) ==========================================
+!=== OPTIONALY: GET THE NUMBER OF FOUND ELEMENTS "n"                                 ==========================================
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+!=== GET THE INDEX IN "rawList" OF THE 1ST APPEARANCE OF ONE OF THE "del(:)" SEPARATORS (0 IF NONE OF THEM ARE PRESENT)
+!===  IF lSc == .TRUE.:  * SKIP HEAD SIGNS OR EXPONENTS SIGNS THAT SHOULD NOT BE CONFUSED WITH SEPARATORS
+!===                     * THEN TEST WHETHER THE STRING FROM START TO THE FOUND SEPARATOR IS A CORRECTLY FORMATTED NUMBER
+!==============================================================================================================================
+LOGICAL FUNCTION strIdx_prv(rawList, del, ibeg, idx, idel, lSc) RESULT(lerr)
+  CHARACTER(LEN=*),  INTENT(IN)  :: rawList                          !--- String in which delimiters have to be identified
+  CHARACTER(LEN=*),  INTENT(IN)  :: del(:)                           !--- List of delimiters
+  INTEGER,           INTENT(IN)  :: ibeg                             !--- Start index
+  INTEGER,           INTENT(OUT) :: idx                              !--- Index of the first identified delimiter in "rawList"
+  INTEGER,           INTENT(OUT) :: idel                             !--- Index of the identified delimiter (0 if idx==0)
+  LOGICAL, OPTIONAL, INTENT(IN)  :: lSc                              !--- Care about nbs with front sign or in scient. notation
+
+  INTEGER               :: idx0                                      !--- Used to display an identified non-numeric string
+  INTEGER, ALLOCATABLE  :: ii(:)
+  LOGICAL               :: ll, ls
+  CHARACTER(LEN=maxlen) :: d
+!  modname = 'strIdx'
+  lerr = .FALSE.
+  idx = strIdx1(rawList, del, ibeg, idel)                            !--- del(idel) appears in "rawList" at position idx
+  IF(.NOT.PRESENT(lSc))               RETURN                         !--- No need to check exceptions for numbers => finished
+  IF(.NOT.        lSc )               RETURN                         !--- No need to check exceptions for numbers => finished
+  IF(idx == 0) THEN                                                  !--- No element of "del" in "rawList":
+    lerr = .NOT.is_numeric(rawList(ibeg:))                           !--- String must be a number
+    IF(lerr) idx = LEN_TRIM(rawList); RETURN                         !--- Update idx => rawList(ibeg:idx-1) is the whole string
+  END IF
+  idx0 = idx
+  IF(test(idx==1.AND.INDEX('+-',del(idel))/=0, lerr)) RETURN         !--- Front separator different from +/-: error
+  IF(idx/=1.AND.is_numeric(rawList(ibeg:idx-1)))      RETURN         !--- The input string tail is a valid number
+  idx = strIdx1(rawList, del, idx+1, idel)                           !---   => TO THE NEXT DELIMITER
+  IF(idx == 0) THEN
+    lerr = .NOT.is_numeric(rawList(ibeg:))                           !--- No delimiter detected: string must be a number
+    IF(lerr) idx = idx0; RETURN
+  END IF
+  idx0 = idx
+  IF(is_numeric(rawList(ibeg:idx-1)))                 RETURN         !--- The input string tail is a valid number
+  IF(test(          INDEX('eE',rawList(idx-1:idx-1)) /= 0  &         !--- Sole possible exception: scientific notation: E+/-
+               .OR. INDEX('+-',del(idel)) /=0, lerr)) RETURN
+  idx = strIdx1(rawList, del, idx+1, idel)                           !---   => TO THE NEXT DELIMITER
+  IF(idx == 0) THEN
+    lerr = .NOT.is_numeric(rawList(ibeg:))                           !--- No separator detected: string must be a number
+    IF(lerr) idx = idx0; RETURN
+  END IF
+  lerr = .NOT.is_numeric(rawList(ibeg:idx-1))
+CONTAINS
+
+INTEGER FUNCTION strIdx1(str, del, ib, id) RESULT(idx)
+  CHARACTER(LEN=*),  INTENT(IN)  :: str
+  CHARACTER(LEN=*),  INTENT(IN)  :: del(:)
+  INTEGER,           INTENT(IN)  :: ib
+  INTEGER,           INTENT(OUT) :: id
+
+  INTEGER              :: nd, ns, i
+  INTEGER, ALLOCATABLE :: ii(:)
+
+  nd  = SIZE(del)                                                    !--- Number of separators
+  ns  = LEN_TRIM(str)                                                !--- Length of the raw chain
+  ii  = [(INDEX( str(ib:ns), del(i) ), i = 1, nd)]                   !--- Determine the next separator start index
+  id  =  MINLOC( ii, MASK = ii /= 0, DIM = 1 )                       !--- Current delimiter index in the "delimiter(:)" list
+  idx = 0
+  IF(ANY(ii /= 0)) idx = MINVAL( ii, MASK = ii /= 0 ) + ib - 1       !--- Index in "str(1:ns)" of the delimiter first character
+  IF(idx == 0) id = 0
+END FUNCTION strIdx1
+
+END FUNCTION strIdx_prv
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+!=== Return the index of first appearance of "del" in "str" starting from index "ib"
+!==============================================================================================================================
+INTEGER FUNCTION strIndex(str, del, ib) RESULT(idx)
+  CHARACTER(LEN=*),  INTENT(IN)  :: str
+  CHARACTER(LEN=*),  INTENT(IN)  :: del
+  INTEGER,           INTENT(IN)  :: ib
+  idx  = INDEX( str(ib:LEN_TRIM(str)), del ) + ib -1
+END FUNCTION strIndex
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+!=== Count the number of elements separated by "delimiter" in list "rawList". =================================================
+!==============================================================================================================================
+LOGICAL FUNCTION strCount_11(rawList, delimiter, nb, lSc) RESULT(out)
+  CHARACTER(LEN=*),  INTENT(IN)  :: rawList
+  CHARACTER(LEN=*),  INTENT(IN)  :: delimiter
+  INTEGER,           INTENT(OUT) :: nb
+  LOGICAL, OPTIONAL, INTENT(IN)  :: lSc
+  LOGICAL :: ll
+  ll = .FALSE.; IF(PRESENT(lSc)) ll = lSc
+  out = strCount_1m(rawList, [delimiter], nb, ll)
+END FUNCTION strCount_11
+!==============================================================================================================================
+LOGICAL FUNCTION strCount_m1(rawList, delimiter, nb, lSc) RESULT(out)
+  CHARACTER(LEN=*),     INTENT(IN)  :: rawList(:)
+  CHARACTER(LEN=*),     INTENT(IN)  :: delimiter
+  INTEGER, ALLOCATABLE, INTENT(OUT) :: nb(:)
+  LOGICAL,    OPTIONAL, INTENT(IN)  :: lSc
+
+  LOGICAL :: ll
+  INTEGER :: id
+
+  ll  = .FALSE.; IF(PRESENT(lSc)) ll = lSc .AND. INDEX('+-', delimiter) /= 0
+  out = .TRUE.
+  ALLOCATE(nb(SIZE(rawList)))
+  DO id = 1, SIZE(rawList)
+    out = out .AND. strCount_1m(rawList(id), [delimiter], nb(id), ll)
+  END DO
+END FUNCTION strCount_m1
+!==============================================================================================================================
+LOGICAL FUNCTION strCount_1m(rawList, delimiter, nb, lSc) RESULT(lerr)
+  CHARACTER(LEN=*),  INTENT(IN)  :: rawList
+  CHARACTER(LEN=*),  INTENT(IN)  :: delimiter(:)
+  INTEGER,           INTENT(OUT) :: nb
+  LOGICAL, OPTIONAL, INTENT(IN)  :: lSc
+  INTEGER              :: ib, ie, jd, nr
+  LOGICAL              :: ll
+  CHARACTER(LEN=1024)  :: r
+!  modname = 'strCount'
+  lerr = .FALSE.
+  ll   = .FALSE.; IF(PRESENT(lSc)) ll = lSc
+  r  = TRIM(ADJUSTL(rawList))
+  nr = LEN_TRIM(r); IF(nr == 0) RETURN
+  nb = 1; ib = 1
+  DO
+    lerr = strIdx_prv(r, delimiter, ib, ie, jd, ll)
+    IF(fmsg('"'//TRIM(r(ib:ie-1))//'" is not numeric', ll=lerr)) RETURN
+    IF(jd == 0) EXIT
+    ib = ie + LEN(delimiter(jd))
+    DO WHILE(r(ib:ib) == ' ' .AND. ib < nr); ib = ib + 1; END DO     !--- Skip spaces before next chain
+    nb = nb + 1
+  END DO
+END FUNCTION strCount_1m
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+!=== Purpose: Parse "delimiter"-separated list "rawList" into the pair keys(:), vals(:).   ====================================
+!===          Corresponding "vals" remains empty if the element does not contain "=" sign. ====================================
+!==============================================================================================================================
+LOGICAL FUNCTION strParse_1(rawList, delimiter, keys, lSc, vals, n) RESULT(lerr)
+  CHARACTER(LEN=*),                             INTENT(IN)  :: rawList, delimiter
+  CHARACTER(LEN=maxlen), ALLOCATABLE,           INTENT(OUT) :: keys(:)
+  LOGICAL,                            OPTIONAL, INTENT(IN)  :: lSc      !--- Take care about numbers in scientific notation
+  CHARACTER(LEN=maxlen), ALLOCATABLE, OPTIONAL, INTENT(OUT) :: vals(:)
+  INTEGER,                            OPTIONAL, INTENT(OUT) :: n
+  LOGICAL :: ll
+!  modname = 'strParse'
+  ll = .FALSE.; IF(PRESENT(lSc)) ll = lSc
+  IF(.NOT.PRESENT(vals)) lerr = strParse_m(rawList, [delimiter], keys, ll)
+  IF(     PRESENT(vals)) lerr = strParse_m(rawList, [delimiter], keys, ll, vals)
+  IF(PRESENT(n)) n = SIZE(keys)
+END FUNCTION strParse_1
+!==============================================================================================================================
+LOGICAL FUNCTION strParse_m(rawList, delimiter, keys, lSc, vals, n, id) RESULT(lerr)
+  CHARACTER(LEN=*),                             INTENT(IN)  :: rawList, delimiter(:)
+  CHARACTER(LEN=maxlen),           ALLOCATABLE, INTENT(OUT) :: keys(:)  !--- Parsed keys vector
+  LOGICAL,               OPTIONAL,              INTENT(IN)  :: lSc      !--- Take care about numbers in scientific notation
+  CHARACTER(LEN=maxlen), OPTIONAL, ALLOCATABLE, INTENT(OUT) :: vals(:)  !--- Values for <name>=<value> keys
+  INTEGER,               OPTIONAL,              INTENT(OUT) :: n        !--- Length of the parsed vector
+  INTEGER,               OPTIONAL, ALLOCATABLE, INTENT(OUT) :: id(:)    !--- Indexes of the separators in "delimiter(:)" vector
+
+  CHARACTER(LEN=1024) :: r
+  INTEGER :: nr, ik, nk, ib, ie, jd
+  LOGICAL :: ll
+
+!  modname = 'strParse'
+  ll = .FALSE.; IF(PRESENT(lSc)) ll = lSc
+  IF(test(fmsg("Couldn't parse list: non-numerical strings were found", ll=strCount_1m(rawList, delimiter, nk, ll)),lerr)) RETURN
+
+  !--- FEW ALLOCATIONS
+  ALLOCATE(keys(nk))
+  IF(PRESENT(vals)) ALLOCATE(vals(nk))
+  IF(PRESENT(id))   ALLOCATE(id(nk-1))
+  IF(PRESENT(n)) n = nk
+
+  !--- PARSING
+  r  = TRIM(ADJUSTL(rawList))
+  nr = LEN_TRIM(r); IF(nr == 0) RETURN
+  ib = 1
+  DO ik = 1, nk-1
+    IF(test(fmsg('Non-numeric values found', ll=strIdx_prv(r, delimiter, ib, ie, jd, ll)),lerr)) RETURN
+    keys(ik) = r(ib:ie-1)
+    IF(PRESENT(vals)) CALL parseKeys(keys(ik), vals(ik))             !--- Parse a <key>=<val> pair
+    IF(PRESENT(id  )) id(ik) = jd                                    !--- Index in "delimiter(:)" of the "ik"th delimiter
+    ib = ie + LEN_TRIM( delimiter(jd) )                              !--- Length of the current delimiter
+    DO WHILE(r(ib:ib) == ' ' .AND. ib < nr); ib = ib + 1; END DO     !--- Skip spaces before next chain
+  END DO
+  keys(nk) = r(ib:nr)
+  IF(PRESENT(vals)) CALL parseKeys(keys(nk), vals(nk))               !--- Parse a <key>=<val> pair
+
+CONTAINS
+
+SUBROUTINE parseKeys(key, val)
+  CHARACTER(LEN=*), INTENT(INOUT) :: key
+  CHARACTER(LEN=*), INTENT(OUT)   :: val
+  INTEGER :: ix
+  ix = INDEX(key, '='); IF(ix == 0) RETURN                           !--- First "=" index in "key"
+  val = ADJUSTL(key(ix+1:LEN_TRIM(key)))
+  key = ADJUSTL(key(1:ix-1))
+END SUBROUTINE parseKeys
+
+END FUNCTION strParse_m    
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+!=== String substitution: replace "key" by "val" each time it appears in "str".
+!==============================================================================================================================
+SUBROUTINE strReplace_1(str, key, val, lsurr)
+  CHARACTER(LEN=*),  INTENT(INOUT) :: str        !--- Main string
+  CHARACTER(LEN=*),  INTENT(IN)    :: key, val   !--- "key" will be replaced by "val"
+  LOGICAL, OPTIONAL, INTENT(IN)    :: lsurr      !--- TRUE => key must be surrounded by special characters to be substituted
+
+  CHARACTER(LEN=1024) :: s, t
+  INTEGER :: i0, ix, nk, ns
+  LOGICAL :: lsur, lb, le
+
+  lsur = .FALSE.; IF(PRESENT(lsurr)) lsur = lsurr
+  nk = LEN_TRIM(key)
+  i0 = 1
+  DO
+    ns = LEN_TRIM(str)
+    ix = INDEX(str(i0:ns), TRIM(key))            !--- First appearance index of "key" in "s", starting from index "i0"
+    IF(ix == 0) EXIT
+    ix = ix + i0 -1
+    IF(lsur) THEN                                !--- Key must be surrounded by special characters
+      !--- lb=.TRUE.: key is at the very beginning of "str" or located after  a special character
+      lb = ix     ==1;  IF(.NOT.lb) lb = INDEX('+-*/()^', str(ix-1 :ix-1 ))/=0
+      !--- le=.TRUE.: key is at the very end       of "str" or located before a special character
+      le = ix+nk-1==ns; IF(.NOT.le) le = INDEX('+-*/()^', str(ix+nk:ix+nk))/=0
+      IF(.NOT.(lb.AND.le)) THEN; i0 = i0 + nk; CYCLE; END IF
+    END IF
+    str = str(1:ix-1)//TRIM(val)//str(ix+nk:ns)
+  END DO
+END SUBROUTINE strReplace_1
+!==============================================================================================================================
+SUBROUTINE strReplace_m(str, key, val, lsurr)
+  CHARACTER(LEN=*),  INTENT(INOUT) :: str(:)     !--- Main strings vector
+  CHARACTER(LEN=*),  INTENT(IN)    :: key, val   !--- "key" will be replaced by "val"
+  LOGICAL, OPTIONAL, INTENT(IN)    :: lsurr      !--- TRUE => key must be surrounded by special characters to be substituted
+  INTEGER :: k
+  LOGICAL :: ll
+  ll=.FALSE.; IF(PRESENT(lsurr)) ll=lsurr
+  DO k=1, SIZE(str); CALL strReplace_1(str(k),key,val,ll); END DO
+END SUBROUTINE strReplace_m
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+!=== Contatenate horizontally scalars/vectors of strings/integers/reals into a vector/array ===================================
+!==============================================================================================================================
+FUNCTION horzcat_s1(s0, s1, s2, s3, s4, s5, s6, s7, s8, s9) RESULT(out)
+  CHARACTER(LEN=*),           TARGET, INTENT(IN) :: s0
+  CHARACTER(LEN=*), OPTIONAL, TARGET, INTENT(IN) :: s1, s2, s3, s4, s5, s6, s7, s8, s9
+  CHARACTER(LEN=maxlen), ALLOCATABLE :: out(:)
+  CHARACTER(LEN=maxlen), POINTER     :: s
+  LOGICAL :: lv(10)
+  INTEGER :: iv
+  lv = [   .TRUE.   , PRESENT(s1), PRESENT(s2), PRESENT(s3), PRESENT(s4) , &
+         PRESENT(s5), PRESENT(s6), PRESENT(s7), PRESENT(s8), PRESENT(s9) ]
+  ALLOCATE(out(COUNT(lv)))
+  DO iv=1, COUNT(lv)
+    SELECT CASE(iv)
+      CASE(1); s=> s0; CASE(2); s=> s1; CASE(3); s=> s2; CASE(4); s=> s3; CASE(5); s=> s4
+      CASE(6); s=> s5; CASE(7); s=> s6; CASE(8); s=> s7; CASE(9); s=> s8; CASE(10);s=> s9
+    END SELECT
+    out(iv) = s
+  END DO
+END FUNCTION horzcat_s1
+!==============================================================================================================================
+FUNCTION horzcat_sm(s0, s1, s2, s3, s4, s5, s6, s7, s8, s9) RESULT(out)
+  CHARACTER(LEN=*),           TARGET, DIMENSION(:), INTENT(IN) :: s0
+  CHARACTER(LEN=*), OPTIONAL, TARGET, DIMENSION(:), INTENT(IN) :: s1, s2, s3, s4, s5, s6, s7, s8, s9
+  CHARACTER(LEN=maxlen), ALLOCATABLE :: out(:,:)
+  CHARACTER(LEN=maxlen), POINTER     :: s(:)
+  LOGICAL :: lv(10)
+  INTEGER :: nrow, ncol, iv, n
+  lv = [   .TRUE.   , PRESENT(s1), PRESENT(s2), PRESENT(s3), PRESENT(s4) , &
+         PRESENT(s5), PRESENT(s6), PRESENT(s7), PRESENT(s8), PRESENT(s9) ]
+  nrow = SIZE(s0); ncol=COUNT(lv)
+  ALLOCATE(out(nrow, ncol))
+  DO iv=1, ncol
+    SELECT CASE(iv)
+      CASE(1); s=> s0; CASE(2); s=> s1; CASE(3); s=> s2; CASE(4); s=> s3; CASE(5); s=> s4
+      CASE(6); s=> s5; CASE(7); s=> s6; CASE(8); s=> s7; CASE(9); s=> s8; CASE(10);s=> s9
+    END SELECT
+    n = SIZE(s, DIM=1)
+    IF(n/=nrow) THEN; CALL msg("Can't concatenate vectors of differing lengths"); STOP; END IF
+    out(:,iv) = s(:)
+  END DO
+END FUNCTION horzcat_sm
+!==============================================================================================================================
+FUNCTION horzcat_i1(i0, i1, i2, i3, i4, i5, i6, i7, i8, i9) RESULT(out)
+  INTEGER,           TARGET, INTENT(IN) :: i0
+  INTEGER, OPTIONAL, TARGET, INTENT(IN) :: i1, i2, i3, i4, i5, i6, i7, i8, i9
+  INTEGER, ALLOCATABLE :: out(:)
+  INTEGER, POINTER     :: i
+  LOGICAL :: lv(10)
+  INTEGER :: iv
+  lv = [   .TRUE.   , PRESENT(i1), PRESENT(i2), PRESENT(i3), PRESENT(i4) , &
+         PRESENT(i5), PRESENT(i6), PRESENT(i7), PRESENT(i8), PRESENT(i9) ]
+  ALLOCATE(out(COUNT(lv)))
+  DO iv=1, COUNT(lv)
+    SELECT CASE(iv)
+      CASE(1); i=> i0; CASE(2); i=> i1; CASE(3); i=> i2; CASE(4); i=> i3; CASE(5); i=> i4
+      CASE(6); i=> i5; CASE(7); i=> i6; CASE(8); i=> i7; CASE(9); i=> i8; CASE(10);i=> i9
+    END SELECT
+    out(iv) = i
+  END DO
+END FUNCTION horzcat_i1
+!==============================================================================================================================
+FUNCTION horzcat_im(i0, i1, i2, i3, i4, i5, i6, i7, i8, i9) RESULT(out)
+  INTEGER,           TARGET, DIMENSION(:), INTENT(IN) :: i0
+  INTEGER, OPTIONAL, TARGET, DIMENSION(:), INTENT(IN) :: i1, i2, i3, i4, i5, i6, i7, i8, i9
+  INTEGER, ALLOCATABLE :: out(:,:)
+  INTEGER, POINTER     :: i(:)
+  LOGICAL :: lv(10)
+  INTEGER :: nrow, ncol, iv, n
+  lv = [   .TRUE.   , PRESENT(i1), PRESENT(i2), PRESENT(i3), PRESENT(i4) , &
+         PRESENT(i5), PRESENT(i6), PRESENT(i7), PRESENT(i8), PRESENT(i9) ]
+  nrow = SIZE(i0); ncol=COUNT(lv)
+  ALLOCATE(out(nrow, ncol))
+  DO iv=1, ncol
+    SELECT CASE(iv)
+      CASE(1); i=> i0; CASE(2); i=> i1; CASE(3); i=> i2; CASE(4); i=> i3; CASE(5); i=> i4
+      CASE(6); i=> i5; CASE(7); i=> i6; CASE(8); i=> i7; CASE(9); i=> i8; CASE(10);i=> i9
+    END SELECT
+    n = SIZE(i, DIM=1)
+    IF(n/=nrow) THEN; CALL msg("Can't concatenate vectors of differing lengths"); STOP; END IF
+    out(:,iv) = i(:)
+  END DO
+END FUNCTION horzcat_im
+!==============================================================================================================================
+FUNCTION horzcat_r1(r0, r1, r2, r3, r4, r5, r6, r7, r8, r9) RESULT(out)
+  REAL,           TARGET, INTENT(IN) :: r0
+  REAL, OPTIONAL, TARGET, INTENT(IN) :: r1, r2, r3, r4, r5, r6, r7, r8, r9
+  REAL, ALLOCATABLE :: out(:)
+  REAL, POINTER     :: r
+  LOGICAL :: lv(10)
+  INTEGER :: iv
+  lv = [   .TRUE.   , PRESENT(r1), PRESENT(r2), PRESENT(r3), PRESENT(r4) , &
+         PRESENT(r5), PRESENT(r6), PRESENT(r7), PRESENT(r8), PRESENT(r9) ]
+  ALLOCATE(out(COUNT(lv)))
+  DO iv=1, COUNT(lv)
+    SELECT CASE(iv)
+      CASE(1); r=> r0; CASE(2); r=> r1; CASE(3); r=> r2; CASE(4); r=> r3; CASE(5); r=> r4
+      CASE(6); r=> r5; CASE(7); r=> r6; CASE(8); r=> r7; CASE(9); r=> r8; CASE(10);r=> r9
+    END SELECT
+    out(iv) = r
+  END DO
+END FUNCTION horzcat_r1
+!==============================================================================================================================
+FUNCTION horzcat_rm(r0, r1, r2, r3, r4, r5, r6, r7, r8, r9) RESULT(out)
+  REAL,           TARGET, DIMENSION(:), INTENT(IN) :: r0
+  REAL, OPTIONAL, TARGET, DIMENSION(:), INTENT(IN) :: r1, r2, r3, r4, r5, r6, r7, r8, r9
+  REAL, ALLOCATABLE :: out(:,:)
+  REAL, POINTER     :: r(:)
+  LOGICAL :: lv(10)
+  INTEGER :: nrow, ncol, iv, n
+  lv = [   .TRUE.   , PRESENT(r1), PRESENT(r2), PRESENT(r3), PRESENT(r4) , &
+         PRESENT(r5), PRESENT(r6), PRESENT(r7), PRESENT(r8), PRESENT(r9) ]
+  nrow = SIZE(r0); ncol=COUNT(lv)
+  ALLOCATE(out(nrow, ncol))
+  DO iv=1, ncol
+    SELECT CASE(iv)
+      CASE(1); r=> r0; CASE(2); r=> r1; CASE(3); r=> r2; CASE(4); r=> r3; CASE(5); r=> r4
+      CASE(6); r=> r5; CASE(7); r=> r6; CASE(8); r=> r7; CASE(9); r=> r8; CASE(10);r=> r9
+    END SELECT
+    n = SIZE(r, DIM=1)
+    IF(n/=nrow) THEN; CALL msg("Can't concatenate vectors of differing lengths"); STOP; END IF
+    out(:,iv) = r(:)
+  END DO
+END FUNCTION horzcat_rm
+!==============================================================================================================================
+FUNCTION horzcat_d1(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9) RESULT(out)
+  DOUBLE PRECISION,           TARGET, INTENT(IN) :: d0
+  DOUBLE PRECISION, OPTIONAL, TARGET, INTENT(IN) :: d1, d2, d3, d4, d5, d6, d7, d8, d9
+  DOUBLE PRECISION, ALLOCATABLE :: out(:)
+  DOUBLE PRECISION, POINTER     :: d
+  LOGICAL :: lv(10)
+  INTEGER :: iv
+  lv = [   .TRUE.   , PRESENT(d1), PRESENT(d2), PRESENT(d3), PRESENT(d4) , &
+         PRESENT(d5), PRESENT(d6), PRESENT(d7), PRESENT(d8), PRESENT(d9) ]
+  ALLOCATE(out(COUNT(lv)))
+  DO iv=1, COUNT(lv)
+    SELECT CASE(iv)
+      CASE(1); d=> d0; CASE(2); d=> d1; CASE(3); d=> d2; CASE(4); d=> d3; CASE(5); d=> d4
+      CASE(6); d=> d5; CASE(7); d=> d6; CASE(8); d=> d7; CASE(9); d=> d8; CASE(10);d=> d9
+    END SELECT
+    out(iv) = d
+  END DO
+END FUNCTION horzcat_d1
+!==============================================================================================================================
+FUNCTION horzcat_dm(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9) RESULT(out)
+  DOUBLE PRECISION,           TARGET, DIMENSION(:), INTENT(IN) :: d0
+  DOUBLE PRECISION, OPTIONAL, TARGET, DIMENSION(:), INTENT(IN) :: d1, d2, d3, d4, d5, d6, d7, d8, d9
+  DOUBLE PRECISION, ALLOCATABLE :: out(:,:)
+  DOUBLE PRECISION, POINTER     :: d(:)
+  LOGICAL :: lv(10)
+  INTEGER :: nrow, ncol, iv, n
+  lv = [   .TRUE.   , PRESENT(d1), PRESENT(d2), PRESENT(d3), PRESENT(d4) , &
+         PRESENT(d5), PRESENT(d6), PRESENT(d7), PRESENT(d8), PRESENT(d9) ]
+  nrow = SIZE(d0); ncol=COUNT(lv)
+  ALLOCATE(out(nrow, ncol))
+  DO iv=1, ncol
+    SELECT CASE(iv)
+      CASE(1); d=> d0; CASE(2); d=> d1; CASE(3); d=> d2; CASE(4); d=> d3; CASE(5); d=> d4
+      CASE(6); d=> d5; CASE(7); d=> d6; CASE(8); d=> d7; CASE(9); d=> d8; CASE(10);d=> d9
+    END SELECT
+    n = SIZE(d, DIM=1)
+    IF(n/=nrow) THEN; CALL msg("Can't concatenate vectors of differing lengths"); STOP; END IF
+    out(:,iv) = d(:)
+  END DO
+END FUNCTION horzcat_dm
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+!--- Display a clean table composed of successive vectors of same length.
+!=== The profile "p" describe in which order to pick up the columns from "s", "i" and "r" for display.
+!==============================================================================================================================
+LOGICAL FUNCTION dispTable(p, titles, s, i, r, rFmt, nmax, unit) RESULT(lerr)
+  CHARACTER(LEN=*),           INTENT(IN)  :: p             !--- DISPLAY MAP: s/i/r
+  CHARACTER(LEN=*),           INTENT(IN)  :: titles(:)     !--- TITLES (ONE EACH COLUMN)
+  CHARACTER(LEN=*), OPTIONAL, INTENT(IN)  :: s(:,:)        !--- STRINGS
+  INTEGER,          OPTIONAL, INTENT(IN)  :: i(:,:)        !--- INTEGERS
+  REAL,             OPTIONAL, INTENT(IN)  :: r(:,:)        !--- REALS
+  CHARACTER(LEN=*), OPTIONAL, INTENT(IN)  :: rFmt          !--- Format for reals
+  INTEGER,          OPTIONAL, INTENT(IN)  :: nmax          !--- Display less than "nrow" rows
+  INTEGER,          OPTIONAL, INTENT(IN)  :: unit          !--- Output unit (default: screen)
+
+  CHARACTER(LEN=2048) :: row
+  CHARACTER(LEN=maxlen)  :: rFm, el
+  CHARACTER(LEN=maxlen), ALLOCATABLE :: d(:,:)
+  CHARACTER(LEN=1) :: s1, sp
+  INTEGER :: is, ii, ir, np, nrow, unt, ic
+  INTEGER :: ns, ni, nr, ncol, nmx
+  INTEGER, ALLOCATABLE :: n(:)
+  INTEGER, PARAMETER   :: nm=2                             !--- Space between values & columns
+  LOGICAL :: ls, li, lr
+
+!  modname = 'dispTable'
+  rFm = '*';    IF(PRESENT(rFmt)) rFm = rFmt               !--- Specified format for reals
+  unt = lunout; IF(PRESENT(unit)) unt = unit               !--- Specified output unit
+  np = LEN_TRIM(p); ns = 0; ni = 0; nr = 0; ncol = 0
+  ls = PRESENT(s); li = PRESENT(i); lr = PRESENT(r)
+  lerr = .FALSE.; IF(.NOT.ANY([ls,li,lr])) RETURN          !--- Nothing to do
+  sp = '|'                                                 !--- Separator
+
+  !--- CHECK ARGUMENTS COHERENCE
+  lerr = np /= SIZE(titles); IF(fmsg('string "pattern" length and titles list mismatch', ll=lerr)) RETURN
+  IF(ls) THEN; ns = SIZE(s, DIM=1); ncol = ncol + SIZE(s, DIM=2)
+    lerr = COUNT([(p(ic:ic)=='s', ic=1, np)]) /= SIZE(s, DIM=2)
+  END IF
+  IF(li) THEN; ni = SIZE(i, DIM=1); ncol = ncol + SIZE(i, DIM=2)
+    lerr = COUNT([(p(ic:ic)=='i', ic=1, np)]) /= SIZE(i, DIM=2)
+  END IF
+  IF(lr) THEN; nr = SIZE(r, DIM=1); ncol = ncol + SIZE(r, DIM=2)
+    lerr = COUNT([(p(ic:ic)=='r', ic=1, np)]) /= SIZE(r, DIM=2)
+  END IF
+  IF(fmsg('string "pattern" length and arguments number mismatch', ll=lerr)) RETURN
+  lerr = ncol /= SIZE(titles); IF(fmsg('"titles" length and arguments number mismatch', ll=lerr)) RETURN
+  lerr = ls.AND.li.AND.ns/=ni; IF(fmsg('string and integer arguments lengths mismatch', ll=lerr)) RETURN
+  lerr = ls.AND.lr.AND.ns/=nr; IF(fmsg(   'string and real arguments lengths mismatch', ll=lerr)) RETURN
+  lerr = li.AND.lr.AND.ni/=nr; IF(fmsg(  'integer and real arguments lengths mismatch', ll=lerr)) RETURN
+  nrow = MAX(ns,ni,nr)+1
+  nmx = nrow; IF(PRESENT(nmax)) nmx = MIN(nmx,nmax+1)
+
+  !--- Allocate the assembled quantities array
+  ALLOCATE(d(nrow,ncol), n(ncol))
+
+  !--- Assemble the vectors into a strings array in the order indicated by "pattern"
+  is =  1; ii = 1; ir = 1
+  DO ic = 1, ncol
+    d(1,ic) = TRIM(titles(ic))
+    SELECT CASE(p(ic:ic))
+      CASE('s'); d(2:nmx,ic) =          s(:,is)     ; is = is + 1
+      CASE('i'); d(2:nmx,ic) =  int2str(i(:,ii)    ); ii = ii + 1
+      CASE('r'); d(2:nmx,ic) = real2str(r(:,ir),rFm); ir = ir + 1
+    END SELECT
+    n(ic)=0; DO ir=1, nmx; n(ic)=MAX(n(ic), LEN_TRIM(d(ir,ic))); END DO
+  END DO
+  n(:) = n(:) + 2*nm
+
+  !--- Display the strings array as a table
+  DO ir = 1, nmx; row = ''
+    DO ic = 1, ncol; el = d(ir,ic)
+      s1 = sp
+      row = TRIM(row)//REPEAT(' ',nm)//TRIM(el)//REPEAT(' ',n(ic)-LEN_TRIM(el)-nm)//s1
+    END DO
+    nr = LEN_TRIM(row)-1                                             !--- Final separator removed
+    CALL msg(row(1:nr), unit=unt)
+    IF(ir /= 1) CYCLE                                                !--- Titles are underlined
+    row=''; DO ic=1,ncol; row=TRIM(row)//REPEAT('-',n(ic))//'+'; END DO
+    CALL msg(row(1:LEN_TRIM(row)-1), unit=unt)
+  END DO
+
+END FUNCTION dispTable
+!==============================================================================================================================
+
+!==============================================================================================================================
+LOGICAL FUNCTION dispNamelist(unt, p, titles, s, i, r, rFmt, llast) RESULT(lerr)
+  INTEGER,                    INTENT(IN)  :: unt           !--- Output unit
+  CHARACTER(LEN=*),           INTENT(IN)  :: p             !--- DISPLAY MAP: s/i/r
+  CHARACTER(LEN=*),           INTENT(IN)  :: titles(:)     !--- TITLES (ONE EACH COLUMN)
+  CHARACTER(LEN=*), OPTIONAL, INTENT(IN)  :: s(:,:)        !--- STRINGS
+  INTEGER,          OPTIONAL, INTENT(IN)  :: i(:,:)        !--- INTEGERS
+  REAL,             OPTIONAL, INTENT(IN)  :: r(:,:)        !--- REALS
+  CHARACTER(LEN=*), OPTIONAL, INTENT(IN)  :: rFmt          !--- Format for reals
+  LOGICAL,          OPTIONAL, INTENT(IN)  :: llast         !--- Last variable: no final ','
+
+  CHARACTER(LEN=maxlen)  :: rFm, el
+  CHARACTER(LEN=maxlen), ALLOCATABLE :: d(:,:)
+  CHARACTER(LEN=:),      ALLOCATABLE :: sp, row
+  INTEGER :: is, ii, ir, nrow, ic
+  INTEGER :: ns, ni, nr, ncol, np
+  INTEGER, ALLOCATABLE :: n(:)
+  LOGICAL :: ls, li, lr, la
+
+!  modname = 'dispNamelist'
+  rFm = '*';    IF(PRESENT(rFmt)) rFm = rFmt               !--- Specified format for reals
+  ls = PRESENT(s); li = PRESENT(i); lr = PRESENT(r)
+  lerr = .FALSE.; IF(.NOT.ANY([ls,li,lr])) RETURN          !--- Nothing to do
+  la = .FALSE.; IF(PRESENT(llast)) la = llast
+
+  !--- CHECK ARGUMENTS COHERENCE
+  ns = 0; ni = 0; nr = 0; np = LEN_TRIM(p); ncol = 0
+  IF(ls) THEN; ns = SIZE(s, DIM=1); ncol = ncol + SIZE(s, DIM=2)
+    lerr = COUNT([(p(ic:ic)=='s', ic=1, np)]) /= SIZE(s, DIM=2)
+  END IF
+  IF(li) THEN; ni = SIZE(i, DIM=1); ncol = ncol + SIZE(i, DIM=2)
+    lerr = COUNT([(p(ic:ic)=='i', ic=1, np)]) /= SIZE(i, DIM=2)
+  END IF
+  IF(lr) THEN; nr = SIZE(r, DIM=1); ncol = ncol + SIZE(r, DIM=2)
+    lerr = COUNT([(p(ic:ic)=='r', ic=1, np)]) /= SIZE(r, DIM=2)
+  END IF
+  IF(fmsg('string "pattern" length and arguments number mismatch', ll=lerr)) RETURN
+  lerr = ncol /= SIZE(titles); IF(fmsg('"titles" length and arguments number mismatch', ll=lerr)) RETURN
+  lerr = ls.AND.li.AND.ns/=ni; IF(fmsg('string and integer arguments lengths mismatch', ll=lerr)) RETURN
+  lerr = ls.AND.lr.AND.ns/=nr; IF(fmsg(   'string and real arguments lengths mismatch', ll=lerr)) RETURN
+  lerr = li.AND.lr.AND.ni/=nr; IF(fmsg(  'integer and real arguments lengths mismatch', ll=lerr)) RETURN
+
+  !--- Allocate the assembled quantities array
+  nrow = MAX(ns,ni,nr)+1
+  ALLOCATE(d(nrow,ncol), n(ncol))
+
+  !--- Assemble the vectors into a strings array in the order indicated by "pattern"
+  is =  1; ii = 1; ir = 1
+  DO ic = 1, ncol
+    d(1,ic) = TRIM(titles(ic))
+    SELECT CASE(p(ic:ic))
+      CASE('s'); d(2:nrow,ic) =          s(:,is)     ; is = is + 1
+      CASE('i'); d(2:nrow,ic) =  int2str(i(:,ii)    ); ii = ii + 1
+      CASE('r'); d(2:nrow,ic) = real2str(r(:,ir),rFm); ir = ir + 1
+    END SELECT
+    n(ic) = 0; DO ir=1, nrow; n(ic)=MAX(n(ic), LEN_TRIM(d(ir,ic))); END DO
+    IF(needQuotes(d(2,ic)) .AND. ic/=1) n(ic) = n(ic) + 2 !--- For quotes, using second line only
+  END DO
+
+  !--- Display the strings array as a table
+  DO ir = 1, nrow
+    row = ''; sp = '   '; IF(TRIM(d(ir,1)) /= '') sp = ' = '
+    DO ic = 1, ncol
+      el = d(ir,ic); IF(ic /= 1) el = addQuotes_1(el)
+      row = row//TRIM(el)//REPEAT(' ',n(ic)-LEN_TRIM(el))//sp
+      sp = '   '; IF(ic /= ncol-1) CYCLE
+      IF(TRIM(d(MIN(ir+1,nrow),1)) /= '' .AND. (ir /= nrow .OR. .NOT.la)) sp = ' , '
+    END DO
+    WRITE(unt,'(a)')TRIM(row)
+  END DO
+
+  !--- End of section
+  IF(la) THEN
+    WRITE(unt,'(a)')'/'
+    WRITE(unt,'(a)')
+  END IF
+
+END FUNCTION dispNameList
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+LOGICAL FUNCTION dispOutliers_1(ll, a, n, err_msg, nam, subn, nmax, unit) RESULT(lerr)
+! Display outliers list in tables
+! If "nam" is supplied, it means the last index is for tracers => one table each tracer for rank > 2.
+  LOGICAL,                    INTENT(IN)  :: ll(:)                   !--- Linearized mask of outliers
+  REAL,                       INTENT(IN)  ::  a(:)                   !--- Linearized array of values
+  INTEGER,                    INTENT(IN)  ::  n(:)                   !--- Profile before linearization
+
+  CHARACTER(LEN=*), OPTIONAL, INTENT(IN)  :: err_msg, nam(:), subn   !--- Error message, variables and calling subroutine names
+  INTEGER,          OPTIONAL, INTENT(IN)  :: nmax, unit              !--- Maximum number of lines to display (default: all)
+  CHARACTER(LEN=maxlen),      ALLOCATABLE :: ttl(:)
+  LOGICAL,                    ALLOCATABLE :: m(:)
+  INTEGER,                    ALLOCATABLE :: ki(:), kj(:)
+  INTEGER                                 :: i, j, k, rk, rk1, ib, ie, itr, nm, unt, nmx, nv
+  CHARACTER(LEN=maxlen)                   :: mes, sub, fm='(f12.9)', v, s
+  CHARACTER(LEN=maxlen),      ALLOCATABLE :: vnm(:)
+
+  lerr = ANY(ll); IF(.NOT.lerr) RETURN                               !--- No outliers -> finished
+
+  mes = 'outliers found'; IF(PRESENT(err_msg)) mes = err_msg         !--- Error message
+  vnm = ['a'];            IF(PRESENT(nam ))    vnm = nam             !--- Variables names
+  sub = 'dispOutliers';   IF(PRESENT(subn))    sub = subn            !--- Calling subroutine name
+  nmx = SIZE(a);          IF(PRESENT(nmax))    nmx = MIN(nmx,nmax)   !--- Maximum number of lines to print
+  unt = lunout;           IF(PRESENT(unit))    unt = unit            !--- Unit to print messages
+
+  rk = SIZE(n); nv = SIZE(vnm)
+  IF(test(fmsg('SIZE(nam) /= 1 and /= last "n" element', sub, nv /= 1 .AND. nv /= n(rk), unt),lerr)) RETURN
+  IF(test(fmsg('ll" and "a" sizes mismatch',             sub, SIZE(a) /= SIZE(ll),       unt),lerr)) RETURN
+  IF(test(fmsg('profile "n" does not match "a" and "ll', sub, SIZE(a) /= PRODUCT(n),     unt),lerr)) RETURN
+  CALL msg(mes, sub, unit=unt)
+
+  !--- SCALAR CASE: single value to display
+  IF(rk==1.AND.n(1)==1) THEN
+    IF(ll(1)) WRITE(unt,'(a," = ",f12.9)')TRIM(nam(1)),a(1); RETURN
+  END IF
+
+  rk1 = rk; IF(nv==1) rk1 = rk-1                                    !--- Rank of each displayed table
+  SELECT CASE(rk1)                                                  !--- Indices list
+    CASE(1,2); ki = [ (i,i=1,n(1)) ]
+    CASE(3);   ki = [((i,i=1,n(1)),j=1,n(2))]; kj = [((j,i=1,n(1)),j=1,n(2))]
+    CASE DEFAULT; WRITE(unt,*)'Sorry: routine "dispOutliers" is limited to rank 3'; RETURN
+  END SELECT
+
+  !--- VECTOR CASE:  table " name | value " (known names)  /  )  /  " i | a(i) " (unknown names)
+  IF(rk==1) THEN
+    ALLOCATE(ttl(2)); ttl(2) = TRIM(vnm(1))//'(i)'; ttl(1) = 'i'
+    IF(nv == 1) lerr = dispTable('sr', ttl,               s=cat(PACK(nam,ll)), r=cat(PACK(a,ll)), rFmt=fm, nmax=nmax)
+    IF(nv /= 1) lerr = dispTable('ir', ['name ','value'], i=cat(PACK(ki,m)),   r=cat(PACK(a,ll)), rFmt=fm, nmax=nmax)
+    CALL msg("can't display outliers table", sub, lerr, unt)
+    RETURN
+  END IF
+
+  !--- OTHER CASES: one table for each tracer (last index)
+  ttl = [(ACHAR(k), k = 105, 104+rk)]                                !--- Titles list ('i', 'j', 'k'...)
+  s = strStack( ttl(1:rk-1) )                                        !--- Tracer name dummy indexes: (i, j, k, ...
+
+  DO itr=1,n(rk)
+    nm = PRODUCT(n(1:rk-1))                                          !--- number of elements per tracer
+    ie = itr*nm; ib = ie-nm+1; m=ll(ib:ie)                           !--- section bounds for tracer "itr" ; outlayers mask
+    IF(.NOT.ANY(m)) CYCLE                                            !--- no outlayers for tracer "itr"
+    v = TRIM(vnm(MIN(itr,SIZE(vnm))))//'('//TRIM(s)                  !--- "<name>("
+    IF(nv == 1) ttl(rk) = TRIM(v)//','//int2str(itr)//')'            !--- "<name>(i,j,itr)" (single name)
+    IF(nv /= 1) ttl(rk) = TRIM(v)//')'                               !--- "<nam(itr)>(i,j)" (one name each table/itr index)
+    IF(rk==2) lerr = dispTable('ir',  ttl, i=cat(PACK(ki,m)),            r=cat(PACK(a(ib:ie),m)), rFmt=fm, nmax=nmax)
+    IF(rk==3) lerr = dispTable('iir', ttl, i=cat(PACK(ki,m),PACK(kj,m)), r=cat(PACK(a(ib:ie),m)), rFmt=fm, nmax=nmax)
+    CALL msg("can't display outliers table", sub, lerr, unt)
+    IF(lerr) RETURN
+  END DO
+END FUNCTION dispOutliers_1
+!==============================================================================================================================
+LOGICAL FUNCTION dispOutliers_2(ll, a, n, err_msg, nam, subn, nmax, unit) RESULT(lerr)
+! Display outliers list in tables
+! If "nam" is supplied and, it means the last index is for tracers => one table each tracer for rank > 2.
+  LOGICAL,                    INTENT(IN)  :: ll(:)                   !--- Linearized mask of outliers
+  REAL,                       INTENT(IN)  ::  a(:,:)                 !--- Linearized arrays of values stacked along 2nd dim.
+  INTEGER,                    INTENT(IN)  ::  n(:)                   !--- Profile before linearization
+  CHARACTER(LEN=*), OPTIONAL, INTENT(IN)  :: err_msg, nam(:), subn   !--- Error message, variables and calling subroutine names
+  INTEGER,          OPTIONAL, INTENT(IN)  :: nmax, unit              !--- Maximum number of lines to display (default: all)
+
+  CHARACTER(LEN=maxlen)                   :: mes, sub, fm='(f12.9)', prf
+  CHARACTER(LEN=maxlen),      ALLOCATABLE :: ttl(:), vnm(:)
+  LOGICAL,                    ALLOCATABLE :: m(:)
+  INTEGER,                    ALLOCATABLE :: ki(:), kj(:), kl(:)
+  INTEGER                                 :: i, j, k, rk, ib, ie, itr, nm, nv, unt, nmx, rk1
+  REAL,                       ALLOCATABLE :: val(:,:)
+
+  lerr = ANY(ll); IF(.NOT.lerr) RETURN                                    !--- No outliers -> finished
+  rk = SIZE(n); nv = SIZE(a,2)
+  mes = 'outliers found';         IF(PRESENT(err_msg)) mes = err_msg      !--- Error message
+  vnm = [(ACHAR(k+96),k=1,nv)];   IF(PRESENT(nam ))    vnm = nam          !--- Variables names
+  sub = 'dispOutliers';           IF(PRESENT(subn))    sub = subn         !--- Calling subroutine name
+  nmx = SIZE(a);                  IF(PRESENT(nmax))    nmx = MIN(nmx,nmax)!--- Maximum number of lines to print
+  unt = lunout;                   IF(PRESENT(unit))    unt = unit         !--- Unit to print messages
+  lerr = SIZE(vnm) /= nv;         IF(fmsg('SIZE(nam) /= SIZE(a,2)',                  sub, lerr, unt)) RETURN
+  lerr = SIZE(a,1) /= SIZE(ll);   IF(fmsg('"ll" and "a" sizes mismatch',             sub, lerr, unt)) RETURN
+  lerr = SIZE(a,1) /= PRODUCT(n); IF(fmsg('profile "n" does not match "a" and "ll"', sub, lerr, unt)) RETURN
+
+  SELECT CASE(rk1)                                                   !--- Indices list
+    CASE(0); IF(ll(1)) THEN; WRITE(unt,'(a,", ",a," = ",2f12.9)')TRIM(vnm(1)),TRIM(vnm(2)),a(1,1),a(1,2); RETURN; END IF
+    CASE(1); ki = [  (i,i=1,n(1)) ]
+    CASE(2); ki = [ ((i,i=1,n(1)),j=1,n(2))];           kj = [ ((j,i=1,n(1)),j=1,n(2))]
+    CASE(3); ki = [(((i,i=1,n(1)),j=1,n(2)),k=1,n(3))]; kj = [(((j,i=1,n(1)),j=1,n(2)),k=1,n(3))]
+             kl = [(((k,i=1,n(1)),j=1,n(2)),k=1,n(3))]
+    CASE DEFAULT; WRITE(unt,*)'Sorry: routine "dispOutliers_2" is limited to rank 3'; RETURN
+  END SELECT
+
+  ttl = [(ACHAR(k), k = 105, 104+rk), vnm]                           !--- Titles list ('i', 'j', 'k'...'var1', 'var2', ...)
+  prf = REPEAT('i',rk)//REPEAT('r',nv)                               !--- Profile
+  ALLOCATE(val(COUNT(ll),nv)); DO k=1, nv; val(:,k) = PACK(a(:,k),ll); END DO
+  IF(rk == 1) lerr = dispTable(prf, ttl, i = cat(PACK(ki,ll)),             r = val, rFmt=fm, nmax=nmax)
+  IF(rk == 2) lerr = dispTable(prf, ttl, i = cat(PACK(ki,ll),PACK(kj,ll)), r = val, rFmt=fm, nmax=nmax)
+  IF(rk == 3) lerr = dispTable(prf, ttl, i = cat(PACK(ki,ll),PACK(kj,ll),PACK(kl,ll)), &
+                     r = val, rFmt=fm, nmax=nmax)
+  CALL msg("can't display outliers table", sub, lerr, unt)
+END FUNCTION dispOutliers_2
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+!=== Reduce an algebrical expression (basic operations and parenthesis) to a single number (string format) ====================
+!==============================================================================================================================
+LOGICAL FUNCTION reduceExpr_1(str, val) RESULT(lerr)
+  CHARACTER(LEN=*),      INTENT(IN)  :: str
+  CHARACTER(LEN=maxlen), INTENT(OUT) :: val
+
+  CHARACTER(LEN=maxlen)              :: v
+  CHARACTER(LEN=1024)                :: s, vv
+  CHARACTER(LEN=1024), ALLOCATABLE :: vl(:)
+  INTEGER,             ALLOCATABLE :: ip(:)
+  INTEGER :: nl, nn, i, j, im, ix
+  LOGICAL :: ll
+!  modname = 'reduceExpr_1'
+  s = str
+
+  !--- Check wether the parenthesis are correctly formed
+  ll = strCount(s,'(',nl)
+  ll = strCount(s,')',nn)
+  lerr = nl /= nn
+  IF(fmsg('Mismatching number of opening and closing parenthesis: '//TRIM(s), ll=lerr)) RETURN
+  nl = 2*nl-1
+
+  !--- Build vectors ; vl: parenthesis-separated elements ; ip: parenthesis types (1: opening, 2: closing)
+  ALLOCATE(ip(nl-1),vl(nl))
+  j = 1; im = 1
+  DO i = 1, LEN_TRIM(str)
+    ix = INDEX('()', str(i:i))
+    IF(ix == 0) CYCLE
+    ip(j) = ix
+    vl(j) = str(im:i-1)
+    j = j + 1; im = i + 1
+  END DO
+  vl(j) = str(im:LEN_TRIM(str))
+
+  !--- Search for each opening/closing parenthesis pair
+  DO WHILE(nl > 1)
+    i = 1; DO WHILE(ip(i) /= 1 .OR. ip(i+1) /= 2); i = i + 1; END DO !IF(i > SIZE(ip)+1) EXIT;END DO
+    IF(test(reduceExpr_basic(vl(i+1), v), lerr)) RETURN
+    v = TRIM(vl(i))//TRIM(v); IF(i+2<=nl) v=TRIM(v)//TRIM(vl(i+2))
+    vv = v//REPEAT(' ',768)
+    IF(i == 1) THEN;         ip =  ip(3:nl-1);                vl = [            vv, vl(4  :nl)]
+    ELSE IF(i == nl-1) THEN; ip =  ip(1:nl-2);                vl = [vl(1:nl-3), vv            ]
+    ELSE;                    ip = [ip(1: i-1), ip(i+2:nl-1)]; vl = [vl(1: i-1), vv, vl(i+3:nl)]; END IF
+    nl = SIZE(vl)
+  END DO
+  lerr = reduceExpr_basic(vl(1), val)
+END FUNCTION reduceExpr_1
+
+
+!==============================================================================================================================
+!=== Reduce a simple algebrical expression (basic operations, no parenthesis) to a single number (string format) ==============
+!==============================================================================================================================
+LOGICAL FUNCTION reduceExpr_basic(str, val) RESULT(lerr)
+  CHARACTER(LEN=*),      INTENT(IN)  :: str
+  CHARACTER(LEN=*),      INTENT(OUT) :: val
+  DOUBLE PRECISION,      ALLOCATABLE :: vl(:)
+  INTEGER,               ALLOCATABLE :: id(:)
+  CHARACTER(LEN=maxlen), ALLOCATABLE :: ky(:)
+  CHARACTER(LEN=1),      ALLOCATABLE :: op(:)
+
+  CHARACTER(LEN=1024) :: s
+  DOUBLE PRECISION :: v, vm, vp
+  INTEGER      :: i, ni, io
+
+!  modname = 'reduceExpr_basic'
+  lerr = .FALSE.
+  IF(is_numeric(str)) THEN; val=TRIM(str); RETURN; END IF
+  op = ['^','/','*','+','-']                                                   !--- List of recognized operations
+  s = str
+  IF(test(strParse_m(s, op, ky, .TRUE., id = id), lerr)) RETURN                !--- Parse the values
+  vl = str2dble(ky)                                                            !--- Conversion to doubles
+  lerr = ANY(vl >= HUGE(1.d0))
+  IF(fmsg('Some values are non-numeric in: '//TRIM(s), ll=lerr)) RETURN        !--- Non-numerical values found
+  DO io = 1, SIZE(op)                                                          !--- Loop on known operators (order matters !)
+    DO i = SIZE(id), 1, -1                                                     !--- Loop on found operators
+      ni = SIZE(id)
+      IF(id(i) /= io) CYCLE                                                    !--- Current found operator is not op(io)
+      vm = vl(i); vp = vl(i+1)                                                 !--- Couple of values used for current operation
+      SELECT CASE(io)                                                          !--- Perform operation on the two values
+        CASE(1); v = vm**vp  !--- ^
+        CASE(2); v = vm/vp   !--- /
+        CASE(3); v = vm*vp   !--- *
+        CASE(4); v = vm+vp   !--- +
+        CASE(5); v = vm-vp   !--- +
+      END SELECT
+      IF(i == ni) THEN; vl = [vl(1:ni-1), v]; ELSE; vl = [vl(1:i-1), v, vl(i+2:ni+1)]; END IF
+      IF(i == ni) THEN; id =  id(1:ni-1);     ELSE; id = [id(1:i-1),    id(i+1:ni  )]; END IF
+    END DO
+  END DO
+  val = dble2str(vl(1))
+
+END FUNCTION reduceExpr_basic
+!==============================================================================================================================
+
+!==============================================================================================================================
+FUNCTION reduceExpr_m(str, val) RESULT(lerr)
+  LOGICAL,               ALLOCATABLE              :: lerr(:)
+  CHARACTER(LEN=*),                   INTENT(IN)  :: str(:)
+  CHARACTER(LEN=maxlen), ALLOCATABLE, INTENT(OUT) :: val(:)
+  INTEGER :: i
+  ALLOCATE(lerr(SIZE(str)),val(SIZE(str)))
+  lerr(:) = [(reduceExpr_1(str(i), val(i)), i=1, SIZE(str))]
+END FUNCTION reduceExpr_m
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+!=== Check whether a string is a number or not ================================================================================
+!==============================================================================================================================
+ELEMENTAL LOGICAL FUNCTION is_numeric(str) RESULT(out)
+  CHARACTER(LEN=*), INTENT(IN) :: str
+  REAL    :: x
+  INTEGER :: e
+  CHARACTER(LEN=12) :: fmt
+  IF(TRIM(str) == '') THEN; out = .FALSE.; RETURN; END IF
+  WRITE(fmt,'("(f",i0,".0)")') LEN_TRIM(str)
+  READ(str,fmt,IOSTAT=e) x
+  out = e==0 .AND. INDEX('Ee',str(LEN_TRIM(str):LEN_TRIM(str)))==0
+  IF(str == '') out = .FALSE.
+END FUNCTION is_numeric
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+!=== Convert a string into a logical/integer integer or an integer/real into a string =========================================
+!==============================================================================================================================
+ELEMENTAL LOGICAL FUNCTION str2bool(str) RESULT(out)
+  CHARACTER(LEN=*), INTENT(IN) :: str
+  INTEGER :: ierr
+  READ(str,*,IOSTAT=ierr) out
+  IF(ierr==0) RETURN
+  out = ANY(['t     ','true  ','.true.','y     ','yes   ']==strLower(str))
+END FUNCTION str2bool
+!==============================================================================================================================
+ELEMENTAL INTEGER FUNCTION str2int(str) RESULT(out)
+  CHARACTER(LEN=*), INTENT(IN) :: str
+  INTEGER :: ierr
+  READ(str,*,IOSTAT=ierr) out
+  IF(ierr/=0) out = -HUGE(1)
+END FUNCTION str2int
+!==============================================================================================================================
+ELEMENTAL REAL FUNCTION str2real(str) RESULT(out)
+  CHARACTER(LEN=*), INTENT(IN) :: str
+  INTEGER :: ierr
+  READ(str,*,IOSTAT=ierr) out
+  IF(ierr/=0) out = -HUGE(1.)
+END FUNCTION str2real
+!==============================================================================================================================
+ELEMENTAL DOUBLE PRECISION FUNCTION str2dble(str) RESULT(out)
+  CHARACTER(LEN=*), INTENT(IN) :: str
+  INTEGER :: ierr
+  READ(str,*,IOSTAT=ierr) out
+  IF(ierr/=0) out = -HUGE(1.d0)
+END FUNCTION str2dble
+!==============================================================================================================================
+ELEMENTAL CHARACTER(LEN=maxlen) FUNCTION bool2str(b) RESULT(out)
+  LOGICAL, INTENT(IN) :: b
+  WRITE(out,*)b
+  out = ADJUSTL(out)
+END FUNCTION bool2str
+!==============================================================================================================================
+ELEMENTAL CHARACTER(LEN=maxlen) FUNCTION int2str(i, nDigits) RESULT(out)
+  INTEGER,           INTENT(IN) :: i
+  INTEGER, OPTIONAL, INTENT(IN) :: nDigits
+  WRITE(out,*)i
+  out = ADJUSTL(out)
+  IF(.NOT.PRESENT(nDigits)) RETURN
+  IF(nDigits > LEN_TRIM(out)) out = REPEAT('0', nDigits - LEN_TRIM(out))//TRIM(out)
+END FUNCTION int2str
+!==============================================================================================================================
+ELEMENTAL CHARACTER(LEN=maxlen) FUNCTION real2str(r,fmt) RESULT(out)
+  REAL,                       INTENT(IN) :: r
+  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: fmt
+  IF(     PRESENT(fmt)) WRITE(out,fmt)r
+  IF(.NOT.PRESENT(fmt)) WRITE(out, * )r
+  out = ADJUSTL(out)
+END FUNCTION real2str
+!==============================================================================================================================
+ELEMENTAL CHARACTER(LEN=maxlen) FUNCTION dble2str(d,fmt) RESULT(out)
+  DOUBLE PRECISION,           INTENT(IN) :: d
+  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: fmt
+  IF(     PRESENT(fmt)) WRITE(out,fmt)d
+  IF(.NOT.PRESENT(fmt)) WRITE(out, * )d
+  out = ADJUSTL(out)
+END FUNCTION dble2str
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+FUNCTION addQuotes_1(s) RESULT(out)
+  CHARACTER(LEN=*), INTENT(IN)  :: s
+  CHARACTER(LEN=:), ALLOCATABLE :: out
+  IF(needQuotes(s)) THEN; out = "'"//TRIM(s)//"'"; ELSE; out = s; END IF
+END FUNCTION addQuotes_1
+!==============================================================================================================================
+FUNCTION addQuotes_m(s) RESULT(out)
+  CHARACTER(LEN=*), INTENT(IN)  :: s(:)
+  CHARACTER(LEN=:), ALLOCATABLE :: out(:)
+  INTEGER :: k, n
+  n = MAXVAL(LEN_TRIM(s), MASK=.TRUE.)
+  ALLOCATE(CHARACTER(LEN=n) :: out(SIZE(s)))
+  DO k=1,SIZE(s)
+    IF(needQuotes(s(k))) THEN; out(k) = "'"//TRIM(s(k))//"'"; ELSE; out(k) = s(k); END IF
+  END DO
+END FUNCTION addQuotes_m
+!==============================================================================================================================
+ELEMENTAL LOGICAL FUNCTION needQuotes(s) RESULT(out)
+  CHARACTER(LEN=*), INTENT(IN) :: s
+  CHARACTER(LEN=1) :: b, e
+  out = .TRUE.; IF(TRIM(s) == '') RETURN
+  b = s(1:1); e = s(MAX(1,LEN_TRIM(s)):MAX(1,LEN_TRIM(s)))
+  out = .NOT.is_numeric(s) .AND. (b /= "'" .OR. e /= "'") .AND. ( b /= '"' .OR. e /= '"')
+END FUNCTION needQuotes
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+!=== TEST WHETHER A FILE IS PRESENT OR NOT ====================================================================================
+!==============================================================================================================================
+LOGICAL FUNCTION testFile_1(fname) RESULT(out)
+  CHARACTER(LEN=*), INTENT(IN) :: fname
+  INTEGER :: ierr
+  OPEN(90, FILE=fname, FORM='formatted', STATUS='old', IOSTAT=ierr); CLOSE(99)
+  out = ierr/=0
+END FUNCTION testFile_1
+!==============================================================================================================================
+FUNCTION testFile_m(fname) RESULT(out)
+  LOGICAL,         ALLOCATABLE ::   out(:)
+  CHARACTER(LEN=*), INTENT(IN) :: fname(:)
+  INTEGER :: k
+  out = [(testFile_1(fname(k)), k=1, SIZE(fname))]
+END FUNCTION testFile_m
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+!=== DISPLAY "<message>: the following <items> are <reason>" FOLLOWED BY THE LIST OF <str> FOR WHICH <lerr>==T. ===============
+!==============================================================================================================================
+LOGICAL FUNCTION checkList(str, lerr, message, items, reason, nmax) RESULT(out)
+! Purpose: Messages in case a list contains wrong elements (indicated by lerr boolean vector).
+! Note:    Return value "out" is .TRUE. if there are errors (ie at least one element of "lerr" is TRUE).
+  CHARACTER(LEN=*),   INTENT(IN)  :: str(:)
+  LOGICAL,            INTENT(IN)  :: lerr(:)
+  CHARACTER(LEN=*),   INTENT(IN)  :: message, items, reason
+  INTEGER,  OPTIONAL, INTENT(IN)  :: nmax
+  CHARACTER(LEN=maxlen), ALLOCATABLE :: s(:)
+  INTEGER :: i, nmx
+  nmx = 256; IF(PRESENT(nmax)) nmx=nmax
+  out = ANY(lerr); IF(.NOT.out) RETURN
+  CALL msg(TRIM(message)//': the following '//TRIM(items)//' are '//TRIM(reason)//':')
+  s = strStackm(PACK(str, MASK=lerr), ', ',nmx)
+  DO i=1,SIZE(s,DIM=1); CALL msg(s(i)); END DO
+END FUNCTION checkList
+!==============================================================================================================================
+
+
+!==============================================================================================================================
+!=== Remove comment in line "str", ie all the characters from the first "#" sign found in "str". ==============================
+!==============================================================================================================================
+SUBROUTINE removeComment(str)
+  CHARACTER(LEN=*), INTENT(INOUT) :: str
+  INTEGER :: ix
+  ix = INDEX(str,'# '); IF(ix /= 0) str = str(1:ix-1)//REPEAT(' ',LEN(str)-ix+1)
+END SUBROUTINE removeComment
+!==============================================================================================================================
+
+
+
+END MODULE strings_mod
Index: LMDZ6/trunk/libf/misc/trac_types_mod.F90
===================================================================
--- LMDZ6/trunk/libf/misc/trac_types_mod.F90	(revision 4046)
+++ LMDZ6/trunk/libf/misc/trac_types_mod.F90	(revision 4046)
@@ -0,0 +1,55 @@
+MODULE trac_types_mod
+
+  USE strings_mod, ONLY: maxlen
+  PRIVATE
+
+!=== TRACERS DESCRIPTOR DERIVED TYPE AND ASSOCIATED ROUTINES INTERFACES =======================================================
+  PUBLIC :: trac_type, isot_type, keys_type
+!------------------------------------------------------------------------------------------------------------------------------
+  TYPE :: keys_type                                        !=== TYPE FOR A SET OF KEYS ASSOCIATED TO AN ELEMENT
+    CHARACTER(LEN=maxlen)              :: name             !--- Tracer name
+    CHARACTER(LEN=maxlen), ALLOCATABLE :: key(:)           !--- Keys string list
+    CHARACTER(LEN=maxlen), ALLOCATABLE :: val(:)           !--- Corresponding values string list
+  END TYPE keys_type
+!------------------------------------------------------------------------------------------------------------------------------
+  TYPE :: trac_type                                        !=== TYPE FOR A SINGLE TRACER NAMED "name"
+    CHARACTER(LEN=maxlen) :: name        = ''              !--- Name of the tracer
+    CHARACTER(LEN=maxlen) :: gen0Name    = ''              !--- First generation ancestor name
+    CHARACTER(LEN=maxlen) :: parent      = ''              !--- Parent name
+    CHARACTER(LEN=maxlen) :: longName    = ''              !--- Long name (with advection scheme suffix)
+    CHARACTER(LEN=maxlen) :: type        = 'tracer'        !--- Type  (so far: 'tracer' / 'tag')
+    CHARACTER(LEN=maxlen) :: phase       = 'g'             !--- Phase ('g'as / 'l'iquid / 's'olid)
+    CHARACTER(LEN=maxlen) :: component                     !--- Coma-separated list of components (Ex: lmdz,inca)
+    INTEGER               :: iadv        = 10              !--- Advection scheme used
+    INTEGER               :: iGeneration = 1               !--- Generation number (>=1)
+    LOGICAL               :: isAdvected  = .FALSE.         !--- "true" tracers: iadv > 0 . COUNT(     isAdvected) =nqtrue
+    LOGICAL               :: isH2Ofamily = .FALSE.         !--- H2O tracers/isotopes/tags. COUNT(.NOT.isH2Ofamily)=nqtottr
+    INTEGER               :: iqParent    = 0               !--- Parent index
+    INTEGER,  ALLOCATABLE :: iqDescen(:)                   !--- Descendants index (in growing generation order)
+    INTEGER               :: nqDescen    = 0               !--- Number of descendants (all generations)
+    INTEGER               :: nqChilds    = 0               !--- Number of childs    (first generation)
+    INTEGER               :: iso_iGroup  = 0               !--- Isotopes group index in isotopes(:)
+    INTEGER               :: iso_iName   = 0               !--- Isotope  name  index in isotopes(iso_iGroup)%trac(:)
+    INTEGER               :: iso_iZone   = 0               !--- Isotope  zone  index in isotopes(iso_iGroup)%zone(:)
+    INTEGER               :: iso_iPhase  = 0               !--- Isotope  phase index in isotopes(iso_iGroup)%phas
+    TYPE(keys_type)       :: keys                          !--- <key>=<val> pairs vector
+  END TYPE trac_type
+!------------------------------------------------------------------------------------------------------------------------------
+  TYPE :: isot_type                                        !=== TYPE FOR AN ISOTOPES FAMILY DESCENDING ON TRACER "parent"
+    CHARACTER(LEN=maxlen)              :: parent           !--- Isotopes family name (parent tracer name ; ex: H2O)
+    LOGICAL                            :: check=.FALSE.    !--- Triggering of the checking routines
+    TYPE(keys_type),       ALLOCATABLE :: keys(:)          !--- Isotopes keys/values pairs list     (length: niso)
+    CHARACTER(LEN=maxlen), ALLOCATABLE :: trac(:)          !--- Isotopes + tagging tracers list     (length: nitr)
+    CHARACTER(LEN=maxlen), ALLOCATABLE :: zone(:)          !--- Geographic tagging zones names list (length: nzon)
+    CHARACTER(LEN=maxlen)              :: phase = 'g'      !--- Phases list: [g][l][s]              (length: npha)
+    INTEGER                            :: niso = 0         !--- Number of isotopes, excluding tagging tracers
+    INTEGER                            :: nzon = 0         !--- Number of geographic tagging zones
+    INTEGER                            :: nitr = 0         !--- Number of isotopes, including tagging tracers
+    INTEGER                            :: npha = 0         !--- Number phases
+    INTEGER,               ALLOCATABLE :: iTraPha(:,:)     !--- Idx in "trac(1:niso)" = f(name(1:nitr)),phas)
+                                                           !---        "iTraPha" former name: "iqiso"
+    INTEGER,               ALLOCATABLE :: iZonIso(:,:)     !--- Idx in "trac(1:nitr)" = f(zone, name(1:niso))
+                                                           !---        "iZonIso" former name: "index_trac"
+  END TYPE isot_type
+
+END MODULE trac_types_mod
Index: LMDZ6/trunk/libf/phylmd/Dust/phytracr_spl_mod.F90
===================================================================
--- LMDZ6/trunk/libf/phylmd/Dust/phytracr_spl_mod.F90	(revision 4045)
+++ LMDZ6/trunk/libf/phylmd/Dust/phytracr_spl_mod.F90	(revision 4046)
@@ -1416,20 +1416,12 @@
        !print *,nbtr
        do it=1,nbtr
-        print *, it, tname(it+nqo)
-        if (tname(it+nqo) == 'PREC' ) then
-            id_prec=it
-        endif
-        if (tname(it+nqo) == 'FINE' ) then
-            id_fine=it
-        endif
-        if (tname(it+nqo) == 'COSS' ) then
-            id_coss=it
-        endif
-        if (tname(it+nqo) == 'CODU' ) then
-            id_codu=it
-        endif
-        if (tname(it+nqo) == 'SCDU' ) then
-            id_scdu=it
-        endif
+        print *, it, tracers(it+nqo)%name
+        SELECT CASE(tracers(it+nqo)%name)
+          CASE('PREC'); id_prec=it
+          CASE('FINE'); id_fine=it
+          CASE('COSS'); id_coss=it
+          CASE('CODU'); id_codu=it
+          CASE('SCDU'); id_scdu=it
+        END SELECT
        enddo
        ! check consistency with dust emission scheme:
Index: LMDZ6/trunk/libf/phylmd/Dust/splaeropt_5wv_rrtm.F90
===================================================================
--- LMDZ6/trunk/libf/phylmd/Dust/splaeropt_5wv_rrtm.F90	(revision 4045)
+++ LMDZ6/trunk/libf/phylmd/Dust/splaeropt_5wv_rrtm.F90	(revision 4046)
@@ -113,26 +113,12 @@
 
   DO itr=1,nbtr    !--loop over tracers  
-
-    IF (tname(itr+nqo)=='PREC') THEN       !--fine mode accumulation mode
-      CYCLE
-    ELSE IF (tname(itr+nqo)=='FINE') THEN  !--fine mode accumulation mode
-      soluble=.TRUE.
-      spsol=1
-      aerindex=1
-    ELSE IF (tname(itr+nqo)=='COSS') THEN  !--coarse mode sea salt
-      soluble=.TRUE.
-      spsol=2
-      aerindex=2
-    ELSE IF (tname(itr+nqo)=='CODU') THEN  !--coarse mode dust
-      soluble=.FALSE.
-      spinsol=1
-      aerindex=3
-    ELSE IF (tname(itr+nqo)=='SCDU') THEN  !--super coarse mode dust
-      soluble=.FALSE.
-      spinsol=2
-      aerindex=4
-    ELSE
-       CALL abort_physic(modname,'I cannot do aerosol optics for '//tname(itr+nqo),1)
-    ENDIF
+    SELECT CASE(tracers(itr+nqo)%name)
+      CASE('PREC'); CYCLE                                  !--precursor
+      CASE('FINE'); soluble=.TRUE.;  spsol=1; aerindex=1   !--fine mode accumulation mode
+      CASE('COSS'); soluble=.TRUE.;  spsol=2; aerindex=2   !--coarse mode sea salt
+      CASE('CODU'); soluble=.FALSE.; spsol=1; aerindex=3   !--coarse mode dust
+      CASE('SCDU'); soluble=.FALSE.; spsol=2; aerindex=4   !--super coarse mode dust
+      CASE DEFAULT; CALL abort_physic(modname,'I cannot do aerosol optics for '//tracers(itr+nqo)%name,1)
+    END SELECT
 
     DO la=1,las
Index: LMDZ6/trunk/libf/phylmd/Dust/splaeropt_6bands_rrtm.F90
===================================================================
--- LMDZ6/trunk/libf/phylmd/Dust/splaeropt_6bands_rrtm.F90	(revision 4045)
+++ LMDZ6/trunk/libf/phylmd/Dust/splaeropt_6bands_rrtm.F90	(revision 4046)
@@ -165,27 +165,13 @@
   cg_ae(:,:,:,:)=0.
     
-  DO itr=1, nbtr
-
-    IF (tname(itr+nqo)=='PREC') THEN       !--precursor
-      CYCLE
-    ELSE IF (tname(itr+nqo)=='FINE') THEN  !--fine mode accumulation mode
-      soluble=.TRUE.
-      spsol=1
-      aerindex=1
-    ELSE IF (tname(itr+nqo)=='COSS') THEN  !--coarse mode sea salt
-      soluble=.TRUE.
-      spsol=2
-      aerindex=2
-    ELSE IF (tname(itr+nqo)=='CODU') THEN  !--coarse mode dust
-      soluble=.FALSE.
-      spinsol=1
-      aerindex=3
-    ELSE IF (tname(itr+nqo)=='SCDU') THEN  !--super coarse mode dust
-      soluble=.FALSE.
-      spinsol=2
-      aerindex=4
-    ELSE
-       CALL abort_physic(modname,'I cannot do aerosol optics for '//tname(itr+nqo),1)
-    ENDIF
+  DO itr=1,nbtr    !--loop over tracers  
+    SELECT CASE(tracers(itr+nqo)%name)
+      CASE('PREC'); CYCLE                                  !--precursor
+      CASE('FINE'); soluble=.TRUE.;  spsol=1; aerindex=1   !--fine mode accumulation mode
+      CASE('COSS'); soluble=.TRUE.;  spsol=2; aerindex=2   !--coarse mode sea salt
+      CASE('CODU'); soluble=.FALSE.; spsol=1; aerindex=3   !--coarse mode dust
+      CASE('SCDU'); soluble=.FALSE.; spsol=2; aerindex=4   !--super coarse mode dust
+      CASE DEFAULT; CALL abort_physic(modname,'I cannot do aerosol optics for '//tracers(itr+nqo)%name,1)
+    END SELECT
 
     IF (soluble) THEN ! For aerosol soluble components
Index: LMDZ6/trunk/libf/phylmd/Dust/splaeropt_lw_rrtm.F90
===================================================================
--- LMDZ6/trunk/libf/phylmd/Dust/splaeropt_lw_rrtm.F90	(revision 4045)
+++ LMDZ6/trunk/libf/phylmd/Dust/splaeropt_lw_rrtm.F90	(revision 4046)
@@ -55,18 +55,10 @@
     !
     DO itr=1,nbtr
-      !
-      IF (tname(itr+nqo)=='PREC') THEN       !--precursor
-        CYCLE
-      ELSE IF (tname(itr+nqo)=='FINE') THEN  !--fine mode accumulation mode
-        CYCLE
-      ELSE IF (tname(itr+nqo)=='COSS') THEN  !--coarse mode sea salt
-        CYCLE
-      ELSE IF (tname(itr+nqo)=='CODU') THEN  !--coarse mode dust
-        spinsol=1
-      ELSE IF (tname(itr+nqo)=='SCDU') THEN  !--super coarse mode dust
-        spinsol=2
-      ELSE
-         CALL abort_physic(modname,'I cannot do aerosol optics for '//tname(itr+nqo),1)
-      ENDIF
+      SELECT CASE(tracers(itr+nqo)%name)
+        CASE('PREC','FINE''COSS'); CYCLE                   !--precursor or fine/coarde accumulation mode
+        CASE('CODU'); spinsol=1                            !--coarse mode dust
+        CASE('SCDU'); spinsol=2                            !--super coarse mode dust
+        CASE DEFAULT; CALL abort_physic(modname,'I cannot do aerosol optics for '//tracers(itr+nqo)%name,1)
+      END SELECT
       !
       DO inu=1,NLW
Index: LMDZ6/trunk/libf/phylmd/Dust/splaerosol_optic_rrtm.F90
===================================================================
--- LMDZ6/trunk/libf/phylmd/Dust/splaerosol_optic_rrtm.F90	(revision 4045)
+++ LMDZ6/trunk/libf/phylmd/Dust/splaerosol_optic_rrtm.F90	(revision 4046)
@@ -51,5 +51,5 @@
   !
   DO itr=1,nbtr
-    IF (tname(itr+nqo)=='FINE') THEN
+    IF (tracers(itr+nqo)%name=='FINE') THEN
       mass_solu_aero(:,:)    = tr_seri(:,:,itr)
       mass_solu_aero_pi(:,:) = tr_seri(:,:,itr)
Index: LMDZ6/trunk/libf/phylmd/cvltr.F90
===================================================================
--- LMDZ6/trunk/libf/phylmd/cvltr.F90	(revision 4045)
+++ LMDZ6/trunk/libf/phylmd/cvltr.F90	(revision 4046)
@@ -12,5 +12,5 @@
   USE IOIPSL 
   USE dimphy
-  USE infotrac_phy, ONLY : nbtr,tname
+  USE infotrac_phy, ONLY : nbtr
   IMPLICIT NONE 
 !=====================================================================
Index: LMDZ6/trunk/libf/phylmd/cvltr_scav.F90
===================================================================
--- LMDZ6/trunk/libf/phylmd/cvltr_scav.F90	(revision 4045)
+++ LMDZ6/trunk/libf/phylmd/cvltr_scav.F90	(revision 4046)
@@ -13,5 +13,5 @@
   USE IOIPSL 
   USE dimphy
-  USE infotrac_phy, ONLY : nbtr,tname
+  USE infotrac_phy, ONLY : nbtr
   IMPLICIT NONE 
   !=====================================================================
Index: LMDZ6/trunk/libf/phylmd/cvltr_spl.F90
===================================================================
--- LMDZ6/trunk/libf/phylmd/cvltr_spl.F90	(revision 4045)
+++ LMDZ6/trunk/libf/phylmd/cvltr_spl.F90	(revision 4046)
@@ -13,5 +13,5 @@
   USE IOIPSL 
   USE dimphy
-  USE infotrac_phy, ONLY : nbtr,tname
+  USE infotrac_phy, ONLY : nbtr
   IMPLICIT NONE 
 !=====================================================================
Index: LMDZ6/trunk/libf/phylmd/dyn1d/1DUTILS.h
===================================================================
--- LMDZ6/trunk/libf/phylmd/dyn1d/1DUTILS.h	(revision 4045)
+++ LMDZ6/trunk/libf/phylmd/dyn1d/1DUTILS.h	(revision 4046)
@@ -713,5 +713,5 @@
 !!      enddo
       DO iq = 1,nqtot
-        nmq(iq) = trim(tname(iq))
+        nmq(iq) = trim(tracers(iq)%name)
       ENDDO
       print*,'in dyn1deta0 ',fichnom,klon,klev,nqtot
@@ -864,5 +864,5 @@
 !!      nmq(4)="tra2"
       DO iq = 1,nqtot
-        nmq(iq) = trim(tname(iq))
+        nmq(iq) = trim(tracers(iq)%name)
       ENDDO
 
Index: LMDZ6/trunk/libf/phylmd/infotrac_phy.F90
===================================================================
--- LMDZ6/trunk/libf/phylmd/infotrac_phy.F90	(revision 4045)
+++ LMDZ6/trunk/libf/phylmd/infotrac_phy.F90	(revision 4046)
@@ -7,4 +7,6 @@
 ! the dynamics (could be further cleaned) and is initialized using values
 ! provided by the dynamics
+
+  USE readTracFiles_mod, ONLY: trac_type, maxlen, delPhase
 
 ! nqtot : total number of tracers and higher order of moment, water vapor and liquid included
@@ -41,12 +43,7 @@
 !$OMP THREADPRIVATE(nqperes)
 
-! Name variables
-  INTEGER,PARAMETER :: tname_lenmax=128 
-  CHARACTER(len=tname_lenmax), ALLOCATABLE, DIMENSION(:), SAVE :: tname ! tracer short name for restart and diagnostics
-  CHARACTER(len=tname_lenmax+3), ALLOCATABLE, DIMENSION(:), SAVE :: ttext ! tracer long name for diagnostics
-!$OMP THREADPRIVATE(tname,ttext)
-
-!! iadv  : index of trasport schema for each tracer
-!  INTEGER, ALLOCATABLE, DIMENSION(:), SAVE    :: iadv
+! Tracers parameters
+  TYPE(trac_type), TARGET, ALLOCATABLE, SAVE :: tracers(:)
+!$OMP THREADPRIVATE(tracers)
 
 ! niadv : vector keeping the coorspondance between all tracers(nqtot) treated in the 
@@ -107,5 +104,5 @@
 CONTAINS
 
-  SUBROUTINE init_infotrac_phy(nqtot_,nqo_,nbtr_,nqtottr_,nqCO2_,tname_,ttext_,type_trac_,&
+  SUBROUTINE init_infotrac_phy(nqtot_,nqo_,nbtr_,nqtottr_,nqCO2_,tracers_,type_trac_,&
                                niadv_,conv_flg_,pbl_flg_,solsym_,&
                                nqfils_,nqdesc_,nqdesc_tot_,iqfils_,iqpere_,&
@@ -139,6 +136,5 @@
     INTEGER,INTENT(IN) :: id_BIN01_strat_
 #endif
-    CHARACTER(len=*),INTENT(IN) :: tname_(nqtot_) ! tracer short name for restart and diagnostics
-    CHARACTER(len=*),INTENT(IN) :: ttext_(nqtot_) ! tracer long name for diagnostics
+    TYPE(trac_type), INTENT(IN) :: tracers_(nqtot_) ! tracers descriptors
     CHARACTER(len=*),INTENT(IN) :: type_trac_
     INTEGER,INTENT(IN) :: niadv_ (nqtot_) ! equivalent dyn / physique
@@ -179,4 +175,5 @@
     nqCO2=nqCO2_
     nqtottr=nqtottr_
+    ALLOCATE(tracers(nqtot)); tracers(:) = tracers_(:)
 #ifdef CPP_StratAer
     nbtr_bin=nbtr_bin_
@@ -187,8 +184,4 @@
     id_BIN01_strat=id_BIN01_strat_
 #endif
-    ALLOCATE(tname(nqtot))
-    tname(:) = tname_(:)
-    ALLOCATE(ttext(nqtot))
-    ttext(:) = ttext_(:)
     type_trac = type_trac_
     ALLOCATE(niadv(nqtot))
Index: LMDZ6/trunk/libf/phylmd/iophy.F90
===================================================================
--- LMDZ6/trunk/libf/phylmd/iophy.F90	(revision 4045)
+++ LMDZ6/trunk/libf/phylmd/iophy.F90	(revision 4046)
@@ -811,6 +811,6 @@
     IMPLICIT NONE
 
-    CHARACTER(LEN=20)                :: nam_var
-    INTEGER, DIMENSION(nfiles)      :: flag_var
+    CHARACTER(LEN=*), INTENT(INOUT) :: nam_var
+    INTEGER,          INTENT(INOUT) :: flag_var(nfiles)
 
     IF(prt_level>10) WRITE(lunout,*)'Avant getin: nam_var flag_var ',nam_var,flag_var(:)
Index: LMDZ6/trunk/libf/phylmd/phyetat0.F90
===================================================================
--- LMDZ6/trunk/libf/phylmd/phyetat0.F90	(revision 4045)
+++ LMDZ6/trunk/libf/phylmd/phyetat0.F90	(revision 4046)
@@ -23,5 +23,5 @@
   USE geometry_mod, ONLY : longitude_deg, latitude_deg
   USE iostart, ONLY : close_startphy, get_field, get_var, open_startphy
-  USE infotrac_phy, only: nbtr, nqo, type_trac, tname, niadv
+  USE infotrac_phy, only: nbtr, nqo, type_trac, tracers, niadv
   USE traclmdz_mod,    ONLY : traclmdz_from_restart
   USE carbon_cycle_mod, ONLY : carbon_cycle_tr, carbon_cycle_cpl, co2_send
@@ -451,6 +451,6 @@
 !!        iiq=niadv(it+2)                                                           ! jyg
         iiq=niadv(it+nqo)                                                           ! jyg
-        found=phyetat0_get(1,trs(:,it),"trs_"//tname(iiq), &
-              "Surf trac"//tname(iiq),0.)
+        found=phyetat0_get(1,trs(:,it),"trs_"//TRIM(tracers(iiq)%name), &
+                                  "Surf trac"//TRIM(tracers(iiq)%name),0.)
      ENDDO
      CALL traclmdz_from_restart(trs)
Index: LMDZ6/trunk/libf/phylmd/phyredem.F90
===================================================================
--- LMDZ6/trunk/libf/phylmd/phyredem.F90	(revision 4045)
+++ LMDZ6/trunk/libf/phylmd/phyredem.F90	(revision 4046)
@@ -35,5 +35,5 @@
   USE iostart, ONLY: open_restartphy, close_restartphy, enddef_restartphy, put_field, put_var
   USE traclmdz_mod, ONLY : traclmdz_to_restart
-  USE infotrac_phy, ONLY: type_trac, niadv, tname, nbtr, nqo
+  USE infotrac_phy, ONLY: type_trac, niadv, tracers, nbtr, nqo
   USE carbon_cycle_mod, ONLY : carbon_cycle_cpl, co2_send
   USE indice_sol_mod, ONLY: nbsrf, is_oce, is_sic, is_ter, is_lic, epsfra
@@ -329,5 +329,5 @@
 !!        iiq=niadv(it+2)                                                           ! jyg
           iiq=niadv(it+nqo)                                                           ! jyg
-          CALL put_field(pass,"trs_"//tname(iiq), "", trs(:, it))
+          CALL put_field(pass,"trs_"//tracers(iiq)%name, "", trs(:, it))
        END DO
     END IF
@@ -380,5 +380,5 @@
     IF (pass==1) CALL enddef_restartphy
     IF (pass==2) CALL close_restartphy
- ENDDO
+  ENDDO ! DO pass=1,2   ! pass=1 netcdf definition ; pass=2 netcdf write
   
   !$OMP BARRIER
Index: LMDZ6/trunk/libf/phylmd/phys_output_mod.F90
===================================================================
--- LMDZ6/trunk/libf/phylmd/phys_output_mod.F90	(revision 4045)
+++ LMDZ6/trunk/libf/phylmd/phys_output_mod.F90	(revision 4046)
@@ -35,5 +35,6 @@
     USE iophy 
     USE dimphy
-    USE infotrac_phy, ONLY: nqtot, nqo, niadv, tname, ttext, type_trac
+    USE infotrac_phy, ONLY: nqtot, nqo, niadv, tracers, type_trac
+    USE strings_mod,  ONLY: maxlen
     USE ioipsl
     USE phys_cal_mod, only : hour, calend
@@ -95,5 +96,5 @@
     CHARACTER(LEN=4), DIMENSION(nlevSTD)  :: clevSTD
     REAL, DIMENSION(nlevSTD)              :: rlevSTD
-    INTEGER                               :: nsrf, k, iq, iiq, iff, i, j, ilev
+    INTEGER                               :: nsrf, k, iq, iiq, iff, i, j, ilev, jq
     INTEGER                               :: naero
     LOGICAL                               :: ok_veget
@@ -113,4 +114,9 @@
     LOGICAL, DIMENSION(nfiles)            :: phys_out_filekeys
     LOGICAL, DIMENSION(nfiles)            :: phys_out_filestations
+
+    CHARACTER(LEN=50) :: outiso
+    CHARACTER(LEN=20) :: unit
+    CHARACTER(LEN=maxlen) :: tnam, lnam, dn
+    INTEGER :: flag(nfiles)
 
 !!!!!!!!!! stockage dans une region limitee pour chaque fichier !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
@@ -494,85 +500,39 @@
        IF (nqtot>=nqo+1) THEN
 !
-            DO iq=nqo+1,nqtot 
-            iiq=niadv(iq)
-            o_trac(iq-nqo) = ctrl_out((/ 1, 5, 5, 5, 10, 10, 11, 11, 11, 11 /), &
-                           tname(iiq),'Tracer '//ttext(iiq), "-",  &
-                           (/ '', '', '', '', '', '', '', '', '', '' /))
-            o_dtr_vdf(iq-nqo) = ctrl_out((/ 4, 7, 7, 7, 10, 10, 11, 11, 11, 11 /), &
-                              'd'//trim(tname(iq))//'_vdf',  &
-                              'Tendance tracer '//ttext(iiq), "-" , &
-                              (/ '', '', '', '', '', '', '', '', '', '' /))
-
-            o_dtr_the(iq-nqo) = ctrl_out((/ 5, 7, 7, 7, 10, 10, 11, 11, 11, 11 /), &
-                              'd'//trim(tname(iq))//'_the', &
-                              'Tendance tracer '//ttext(iiq), "-", &
-                              (/ '', '', '', '', '', '', '', '', '', '' /))
-
-            o_dtr_con(iq-nqo) = ctrl_out((/ 5, 7, 7, 7, 10, 10, 11, 11, 11, 11 /), &
-                              'd'//trim(tname(iq))//'_con', &
-                              'Tendance tracer '//ttext(iiq), "-", &
-                              (/ '', '', '', '', '', '', '', '', '', '' /))
-
-            o_dtr_lessi_impa(iq-nqo) = ctrl_out((/ 7, 7, 7, 7, 10, 10, 11, 11, 11, 11 /), &
-                                     'd'//trim(tname(iq))//'_lessi_impa', &
-                                     'Tendance tracer '//ttext(iiq), "-", &
-                                     (/ '', '', '', '', '', '', '', '', '', '' /))
-
-            o_dtr_lessi_nucl(iq-nqo) = ctrl_out((/ 7, 7, 7, 7, 10, 10, 11, 11, 11, 11 /), &
-                                     'd'//trim(tname(iq))//'_lessi_nucl', &
-                                     'Tendance tracer '//ttext(iiq), "-", &
-                                     (/ '', '', '', '', '', '', '', '', '', '' /))
-
-            o_dtr_insc(iq-nqo) = ctrl_out((/ 7, 7, 7, 7, 10, 10, 11, 11, 11, 11 /), &
-                               'd'//trim(tname(iq))//'_insc', &
-                               'Tendance tracer '//ttext(iiq), "-", &
-                               (/ '', '', '', '', '', '', '', '', '', '' /))
-
-            o_dtr_bcscav(iq-nqo) = ctrl_out((/ 7, 7, 7, 7, 10, 10, 11, 11, 11, 11 /), &
-                                 'd'//trim(tname(iq))//'_bcscav', &
-                                 'Tendance tracer '//ttext(iiq), "-", &
-                                 (/ '', '', '', '', '', '', '', '', '', '' /))
-
-            o_dtr_evapls(iq-nqo) = ctrl_out((/ 7, 7, 7, 7, 10, 10, 11, 11, 11, 11 /), &
-                                 'd'//trim(tname(iq))//'_evapls', &
-                                 'Tendance tracer '//ttext(iiq), "-", &
-                                 (/ '', '', '', '', '', '', '', '', '', '' /))
-
-            o_dtr_ls(iq-nqo) = ctrl_out((/ 7, 7, 7, 7, 10, 10, 11, 11, 11, 11 /), &
-                             'd'//trim(tname(iq))//'_ls', &
-                             'Tendance tracer '//ttext(iiq), "-", &
-                             (/ '', '', '', '', '', '', '', '', '', '' /))
-
-            o_dtr_trsp(iq-nqo) = ctrl_out((/ 7, 7, 7, 7, 10, 10, 11, 11, 11, 11 /), &
-                               'd'//trim(tname(iq))//'_trsp', &
-                               'Tendance tracer '//ttext(iiq), "-", &
-                               (/ '', '', '', '', '', '', '', '', '', '' /))
-
-            o_dtr_sscav(iq-nqo) = ctrl_out((/ 7, 7, 7, 7, 10, 10, 11, 11, 11, 11 /), &
-                                'd'//trim(tname(iq))//'_sscav', &
-                                'Tendance tracer '//ttext(iiq), "-", &
-                                (/ '', '', '', '', '', '', '', '', '', '' /))
-
-            o_dtr_sat(iq-nqo) = ctrl_out((/ 7, 7, 7, 7, 10, 10, 11, 11, 11, 11 /), &
-                               'd'//trim(tname(iq))//'_sat', &
-                               'Tendance tracer '//ttext(iiq), "-", &
-                               (/ '', '', '', '', '', '', '', '', '', '' /))
-
-            o_dtr_uscav(iq-nqo) = ctrl_out((/ 7, 7, 7, 7, 10, 10, 11, 11, 11, 11 /), &
-                                'd'//trim(tname(iq))//'_uscav', &
-                                'Tendance tracer '//ttext(iiq), "-", &
-                                 (/ '', '', '', '', '', '', '', '', '', '' /))
-
-            o_dtr_dry(iq-nqo) = ctrl_out((/ 7, 7, 7, 7, 10, 10, 11, 11, 11, 11 /), &
-                              'cum'//'d'//trim(tname(iq))//'_dry', &
-                              'tracer tendency dry deposition'//ttext(iiq), "-", &
-                              (/ '', '', '', '', '', '', '', '', '', '' /))
-
-            o_trac_cum(iq-nqo) = ctrl_out((/ 1, 4, 10, 10, 10, 10, 11, 11, 11, 11 /), &
-                               'cum'//tname(iiq),&
-                               'Cumulated tracer '//ttext(iiq), "-", &
-                               (/ '', '', '', '', '', '', '', '', '', '' /))
-            ENDDO
-      ENDIF
+          DO iq=nqo+1,nqtot 
+            iiq=niadv(iq); jq = iq-nqo
+            dn = 'd'//TRIM(tracers(iiq)%name)//'_'
+
+            flag = [1, 5, 5, 5, 10, 10, 11, 11, 11, 11]
+            lnam = 'Tracer '//TRIM(tracers(iiq)%longName)
+            tnam = TRIM(tracers(iiq)%name); o_trac          (jq) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+            flag = [4, 7, 7, 7, 10, 10, 11, 11, 11, 11]
+            lnam = 'Tendance tracer '//TRIM(tracers(iiq)%longName)
+            tnam = TRIM(dn)//'vdf';         o_dtr_vdf       (jq) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+
+            flag = [5, 7, 7, 7, 10, 10, 11, 11, 11, 11]
+            tnam = TRIM(dn)//'the';         o_dtr_the       (jq) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+            tnam = TRIM(dn)//'con';         o_dtr_con       (jq) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+
+            flag = [7, 7, 7, 7, 10, 10, 11, 11, 11, 11]
+            tnam = TRIM(dn)//'lessi_impa';  o_dtr_lessi_impa(jq) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+            tnam = TRIM(dn)//'lessi_nucl';  o_dtr_lessi_nucl(jq) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+            tnam = TRIM(dn)//'insc';        o_dtr_insc      (jq) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+            tnam = TRIM(dn)//'bcscav';      o_dtr_bcscav    (jq) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+            tnam = TRIM(dn)//'evapls';      o_dtr_evapls    (jq) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+            tnam = TRIM(dn)//'ls';          o_dtr_ls        (jq) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+            tnam = TRIM(dn)//'trsp';        o_dtr_trsp      (jq) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+            tnam = TRIM(dn)//'sscav';       o_dtr_sscav     (jq) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+            tnam = TRIM(dn)//'sat';         o_dtr_sat       (jq) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+            tnam = TRIM(dn)//'uscav';       o_dtr_uscav     (jq) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+
+            lnam = 'tracer tendency dry deposition'//TRIM(tracers(iiq)%longName)
+            tnam = 'cum'//TRIM(dn)//'dry';  o_dtr_dry       (jq) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+
+            flag = [1, 4, 10, 10, 10, 10, 11, 11, 11, 11]
+            lnam = 'Cumulated tracer '//TRIM(tracers(iiq)%longName)
+            tnam = 'cum'//TRIM(tracers(iiq)%name); o_trac_cum(jq)= ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+          ENDDO
+       ENDIF
 
    ENDDO !  iff
@@ -594,4 +554,12 @@
       WRITE(lunout,*)'phys_output_open: ends here'
     ENDIF
+
+!  DO iq=nqo+1,nqtot 
+!    iiq=niadv(iq)
+!    dn = 'd'//TRIM(tracers(iiq)%name)//'_'
+!    WRITE(*,'(a,i1,a,10i3)')'trac(',iiq,')%flag = ',o_trac(iiq)%flag
+!    WRITE(*,'(a,i1,a)')'trac(',iiq,')%tnam = '//TRIM(o_trac(iiq)%name)
+!    WRITE(*,'(a,i1,a)')'trac(',iiq,')%lnam = '//TRIM(o_trac(iiq)%description)
+!  END DO
 
   END SUBROUTINE phys_output_open
Index: LMDZ6/trunk/libf/phylmd/phys_output_var_mod.F90
===================================================================
--- LMDZ6/trunk/libf/phylmd/phys_output_var_mod.F90	(revision 4045)
+++ LMDZ6/trunk/libf/phylmd/phys_output_var_mod.F90	(revision 4046)
@@ -5,4 +5,5 @@
 
   USE dimphy
+  USE strings_mod, ONLY: maxlen
   ! Variables outputs pour les ecritures des sorties
   !======================================================================
@@ -121,9 +122,9 @@
 
   TYPE ctrl_out
-     INTEGER,DIMENSION(nfiles)            :: flag
-     CHARACTER(len=20)                    :: name
-     CHARACTER(len=150)                   :: description
-     CHARACTER(len=20)                    :: unit
-     CHARACTER(len=20),DIMENSION(nfiles)  :: type_ecrit
+     INTEGER               :: flag(nfiles)
+     CHARACTER(len=maxlen) :: name
+     CHARACTER(len=maxlen) :: description
+     CHARACTER(len=maxlen) :: unit
+     CHARACTER(len=maxlen) :: type_ecrit(nfiles)
   END TYPE ctrl_out
 
Index: LMDZ6/trunk/libf/phylmd/phys_output_write_mod.F90
===================================================================
--- LMDZ6/trunk/libf/phylmd/phys_output_write_mod.F90	(revision 4045)
+++ LMDZ6/trunk/libf/phylmd/phys_output_write_mod.F90	(revision 4046)
@@ -25,5 +25,6 @@
 
     USE dimphy, ONLY: klon, klev, klevp1
-    USE infotrac_phy, ONLY: nbtr
+    USE infotrac_phy, ONLY: nbtr, nqtot, nqo, type_trac, tracers, niadv
+    USE strings_mod,  ONLY: maxlen
     USE mod_phys_lmdz_para, ONLY: is_north_pole_phy,is_south_pole_phy
     USE mod_grid_phy_lmdz, ONLY : nbp_lon, nbp_lat
@@ -366,5 +367,4 @@
     USE pbl_surface_mod, ONLY: snow
     USE indice_sol_mod, ONLY: nbsrf
-    USE infotrac_phy, ONLY: nqtot, nqo, type_trac, tname, niadv
     USE geometry_mod, ONLY: cell_area, latitude_deg, longitude_deg
     USE surface_data, ONLY: type_ocean, version_ocean, ok_veget, landice_opt
@@ -441,5 +441,5 @@
     INTEGER ISW
     CHARACTER*1 ch1
-    CHARACTER*20 varname
+    CHARACTER(LEN=maxlen) :: varname, dn
 
 #ifdef CPP_XIOS
@@ -454,4 +454,5 @@
 
     INTEGER :: nqup
+
 
     ! On calcul le nouveau tau:
@@ -512,47 +513,52 @@
           DO iq=nqo+1, nqtot
             iiq=niadv(iq)
-            varname=trim(tname(iiq))
-            WRITE (lunout,*) 'XIOS var=', nqo, iq, nqtot, varname
+            dn = 'd'//TRIM(tracers(iiq)%name)//'_'
+            WRITE (lunout,*) 'XIOS var=', nqo, iq, nqtot, tracers(iiq)%name
+
+            unt = "kg kg-1"
+            varname=trim(tracers(iiq)%name)
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg kg-1")
-            varname='d'//trim(tname(iiq))//'_vdf'
+            CALL xios_set_attr(child, name=varname, unit=unt)
+
+            unt = "kg kg-1 s-1"
+            varname=TRIM(dn)//'vdf'
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg kg-1 s-1")
-            varname='d'//trim(tname(iiq))//'_the'
+            CALL xios_set_attr(child, name=varname, unit=unt)
+            varname=TRIM(dn)//'the'
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg kg-1 s-1")
-            varname='d'//trim(tname(iiq))//'_con'
+            CALL xios_set_attr(child, name=varname, unit=unt)
+            varname=TRIM(dn)//'con'
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg kg-1 s-1")
-            varname='d'//trim(tname(iiq))//'_lessi_impa'
+            CALL xios_set_attr(child, name=varname, unit=unt)
+            varname=TRIM(dn)//'lessi_impa'
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg kg-1 s-1")
-            varname='d'//trim(tname(iiq))//'_lessi_nucl'
+            CALL xios_set_attr(child, name=varname, unit=unt)
+            varname=TRIM(dn)//'lessi_nucl'
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg kg-1 s-1")
-            varname='d'//trim(tname(iiq))//'_insc'
+            CALL xios_set_attr(child, name=varname, unit=unt)
+            varname=TRIM(dn)//'insc'
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg kg-1 s-1")
-            varname='d'//trim(tname(iiq))//'_bcscav'
+            CALL xios_set_attr(child, name=varname, unit=unt)
+            varname=TRIM(dn)//'bcscav'
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg kg-1 s-1")
-            varname='d'//trim(tname(iiq))//'_evapls'
+            CALL xios_set_attr(child, name=varname, unit=unt)
+            varname=TRIM(dn)//'evapls'
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg kg-1 s-1")
-            varname='d'//trim(tname(iiq))//'_ls'
+            CALL xios_set_attr(child, name=varname, unit=unt)
+            varname=TRIM(dn)//'ls'
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg kg-1 s-1")
-            varname='d'//trim(tname(iiq))//'_trsp'
+            CALL xios_set_attr(child, name=varname, unit=unt)
+            varname=TRIM(dn)//'trsp'
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg kg-1 s-1")
-            varname='d'//trim(tname(iiq))//'_sscav'
+            CALL xios_set_attr(child, name=varname, unit=unt)
+            varname=TRIM(dn)//'sscav'
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg kg-1 s-1")
-            varname='d'//trim(tname(iiq))//'_sat'
+            CALL xios_set_attr(child, name=varname, unit=unt)
+            varname=TRIM(dn)//'sat'
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg kg-1 s-1")
-            varname='d'//trim(tname(iiq))//'_uscav'
+            CALL xios_set_attr(child, name=varname, unit=unt)
+            varname=TRIM(dn)//'uscav'
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg kg-1 s-1")
+            CALL xios_set_attr(child, name=varname, unit=unt)
           ENDDO
           !On ajoute les variables 2D traceurs par l interface fortran 
@@ -561,11 +567,15 @@
           DO iq=nqo+1, nqtot
             iiq=niadv(iq)
-            varname='cum'//trim(tname(iiq))
+
+            unt = "kg m-2"
+            varname='cum'//trim(tracers(iiq)%name)
             WRITE (lunout,*) 'XIOS var=', iq, nqtot, varname
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg m-2")
-            varname='cumd'//trim(tname(iiq))//'_dry'
+            CALL xios_set_attr(child, name=varname, unit=unt)
+
+            unt = "kg m-2 s-1"
+            varname='cumd'//trim(tracers(iiq)%name)//'_dry'
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg m-2 s-1")
+            CALL xios_set_attr(child, name=varname, unit=unt)
           ENDDO
     ENDIF
@@ -573,4 +583,5 @@
 #endif
 #endif
+
     ! ug la boucle qui suit ne sert qu'une fois, pour l'initialisation, sinon il n'y a toujours qu'un seul passage:
     DO iinit=1, iinitend
@@ -600,5 +611,4 @@
        ENDIf
        CALL histwrite_phy(o_aire, zx_tmp_fi2d)
-
        IF (vars_defined) THEN
           DO i=1, klon
Index: LMDZ6/trunk/libf/phylmd/traclmdz_mod.F90
===================================================================
--- LMDZ6/trunk/libf/phylmd/traclmdz_mod.F90	(revision 4045)
+++ LMDZ6/trunk/libf/phylmd/traclmdz_mod.F90	(revision 4046)
@@ -96,4 +96,5 @@
     USE indice_sol_mod
     USE print_control_mod, ONLY: lunout
+    USE strings_mod, ONLY: strLower
 
 ! Input variables
@@ -175,95 +176,69 @@
 !!       iiq=niadv(it+2)                                                            ! jyg
        iiq=niadv(it+nqo)                                                            ! jyg
-       IF ( tname(iiq) == "RN" ) THEN                                               
-          id_rn=it ! radon
-       ELSE IF ( tname(iiq) == "PB") THEN
-          id_pb=it ! plomb
-! RomP >>> profil initial de PB210 
-     open (ilesfil2,file='prof.pb210',status='old',iostat=irr2)
-     IF (irr2 == 0) THEN 
-      read(ilesfil2,*) kradio2
-      print*,'number of levels for pb210 profile ',kradio2
-      do k=kradio2,1,-1
-       read (ilesfil2,*) plomb(:,k)
-      enddo
-      close(ilesfil2)
-      do k=1,klev
-       do i=1,klon
-         tr_seri(i,k,id_pb)=plomb(i,k)
-!!        print*, 'tr_seri',i,k,tr_seri(i,k,id_pb)
-        enddo
-      enddo
-     ELSE
-       print *, 'Prof.pb210 does not exist: use restart values'
-     ENDIF
-! RomP <<<
-       ELSE IF ( tname(iiq) == "Aga" .OR. tname(iiq) == "AGA" ) THEN
-          ! Age of stratospheric air
-          id_aga=it
-          radio(id_aga) = .FALSE.
-          aerosol(id_aga) = .FALSE.
-          pbl_flg(id_aga) = 0 
-          
-          ! Find the first model layer above 1.5km from the surface
-          IF (klev>=30) THEN
-             lev_1p5km=6   ! NB! This value is for klev=39
-          ELSE IF (klev>=10) THEN
-             lev_1p5km=5   ! NB! This value is for klev=19
-          ELSE
-             lev_1p5km=klev/2
-          END IF
-       ELSE IF ( tname(iiq) == "BE" .OR. tname(iiq) == "Be" .OR.  &
-            tname(iiq) == "BE7" .OR. tname(iiq) == "Be7" ) THEN  
-          ! Recherche du Beryllium 7
-          id_be=it
-          ALLOCATE( srcbe(klon,klev) )
-          radio(id_be) = .TRUE.
-          aerosol(id_be) = .TRUE. ! le Be est un aerosol
-!jyg le 13/03/2013 ; ajout de pplay en argument de init_be
-!!!          CALL init_be(pctsrf,masktr(:,id_be),tautr(id_be),vdeptr(id_be),scavtr(id_be),srcbe)
-          CALL init_be(pctsrf,pplay,masktr(:,id_be),tautr(id_be),vdeptr(id_be),scavtr(id_be),srcbe)
-          WRITE(lunout,*) 'Initialisation srcBe: OK'
-! RomP >>> profil initial de Be7
-      open (ilesfil,file='prof.be7',status='old',iostat=irr)
-      IF (irr == 0) THEN 
-       read(ilesfil,*) kradio
-       print*,'number of levels for Be7 profile ',kradio
-       do k=kradio,1,-1
-        read (ilesfil,*) beryllium(:,k)
-       enddo
-       close(ilesfil)
-       do k=1,klev
-         do i=1,klon
-          tr_seri(i,k,id_be)=beryllium(i,k)
-!!        print*, 'tr_seri',i,k,tr_seri(i,k,id_be)
-         enddo
-       enddo
-     ELSE
-       print *, 'Prof.Be7 does not exist: use restart values'
-     ENDIF
-! RomP <<<
-       ELSE IF (tname(iiq)=="O3" .OR. tname(iiq)=="o3") THEN
-          ! Recherche de l'ozone : parametrization de la chimie par Cariolle
-          id_o3=it
-          CALL alloc_coefoz   ! allocate ozone coefficients
-          CALL press_coefoz   ! read input pressure levels
-       ELSE IF ( tname(iiq) == "pcsat" .OR. tname(iiq) == "Pcsat" ) THEN
-          id_pcsat=it
-       ELSE IF ( tname(iiq) == "pcocsat" .OR. tname(iiq) == "Pcocsat" ) THEN
-          id_pcocsat=it
-       ELSE IF ( tname(iiq) == "pcq" .OR. tname(iiq) == "Pcq" ) THEN
-          id_pcq=it
-       ELSE IF ( tname(iiq) == "pcs0" .OR. tname(iiq) == "Pcs0" ) THEN
-          id_pcs0=it
-          conv_flg(it)=0 ! No transport by convection for this tracer
-       ELSE IF ( tname(iiq) == "pcos0" .OR. tname(iiq) == "Pcos0" ) THEN
-          id_pcos0=it
-          conv_flg(it)=0 ! No transport by convection for this tracer
-       ELSE IF ( tname(iiq) == "pcq0" .OR. tname(iiq) == "Pcq0" ) THEN
-          id_pcq0=it
-          conv_flg(it)=0 ! No transport by convection for this tracer
-       ELSE
-          WRITE(lunout,*) 'This is an unknown tracer in LMDZ : ', trim(tname(iiq))
-       END IF
+print*,'###'//TRIM(tracers(iiq)%name)//'###'
+print*,'###'//TRIM(strLower(tracers(iiq)%name))//'###'
+       SELECT CASE(strLower(tracers(iiq)%name))
+         CASE("rn");      id_rn     = it ! radon
+         CASE("pb");      id_pb     = it ! plomb
+         CASE("aga");     id_aga    = it ! Age of stratospheric air
+         CASE("be","be7");id_be     = it ! Recherche du Beryllium 7
+         CASE("o3");      id_o3     = it ! Recherche de l'ozone
+         CASE("pcsat");   id_pcsat  = it
+         CASE("pcocsat"); id_pcocsat= it
+         CASE("pcq");     id_pcq    = it
+         CASE("pcs0");    id_pcs0   = it
+         CASE("pcos0");   id_pcos0  = it
+         CASE("pcq0");    id_pcq0   = it
+         CASE DEFAULT
+           WRITE(lunout,*) 'This is an unknown tracer in LMDZ : ', trim(tracers(iiq)%name)
+       END SELECT
+
+       SELECT CASE(strLower(tracers(iiq)%name))
+         CASE("pb")                        !--- RomP >>> profil initial de PB210 
+           OPEN(ilesfil2,file='prof.pb210',status='old',iostat=irr2)
+           IF(irr2 == 0) THEN 
+             READ(ilesfil2,*) kradio2
+             WRITE(lunout,*)'number of levels for pb210 profile ',kradio2
+             DO k=kradio2,1,-1; READ (ilesfil2,*) plomb(:,k); END DO
+             CLOSE(ilesfil2)
+             tr_seri(:,:,id_pb) = plomb(:,:)
+           ELSE
+             WRITE(lunout,*)'Prof. Pb210 does not exist: use restart values'
+           END IF
+         CASE("aga")
+           radio(id_aga) = .FALSE.
+           aerosol(id_aga) = .FALSE.
+           pbl_flg(id_aga) = 0 
+           ! Find the first model layer above 1.5km from the surface
+           IF (klev>=30) THEN
+              lev_1p5km=6                  !--- NB: This value is for klev=39
+           ELSE IF (klev>=10) THEN
+              lev_1p5km=5                  !--- NB: This value is for klev=19
+           ELSE
+              lev_1p5km=klev/2
+           END IF
+         CASE("be","be7")
+           ALLOCATE( srcbe(klon,klev) )
+           radio(id_be) = .TRUE.
+           aerosol(id_be) = .TRUE.         !--- Le Be est un aerosol
+           CALL init_be(pctsrf,pplay,masktr(:,id_be),tautr(id_be),vdeptr(id_be),scavtr(id_be),srcbe)
+           WRITE(lunout,*) 'Initialisation srcBe: OK'
+                                           !--- RomP >>> profil initial de Be7
+           OPEN(ilesfil,file='prof.be7',status='old',iostat=irr)
+           IF(irr == 0) THEN
+             READ(ilesfil,*) kradio
+             WRITE(lunout,*)'number of levels for Be7 profile ',kradio
+             DO k=kradio,1,-1; READ(ilesfil,*) beryllium(:,k); END DO
+             CLOSE(ilesfil)
+             tr_seri(:,:,id_be)=beryllium(:,:)
+           ELSE
+             WRITE(lunout,*)'Prof. Be7 does not exist: use restart values'
+           END IF
+         CASE("o3")                         !--- Parametrisation par la chimie de Cariolle
+           CALL alloc_coefoz                !--- Allocate ozone coefficients
+           CALL press_coefoz                !--- Read input pressure levels
+         CASE("pcs0","pcos0","pcq0")
+           conv_flg(it)=0                   !--- No transport by convection for this tracer
+       END SELECT
     END DO
 
@@ -309,5 +284,5 @@
        IF (zero) THEN
           ! The tracer was not found in restart file or it was equal zero everywhere.
-          WRITE(lunout,*) "The tracer ",trim(tname(iiq))," will be initialized"
+          WRITE(lunout,*) "The tracer ",trim(tracers(iiq)%name)," will be initialized"
           IF (it==id_pcsat .OR. it==id_pcq .OR. &
                it==id_pcs0 .OR. it==id_pcq0) THEN
Index: LMDZ6/trunk/libf/phylmdiso/infotrac_phy.F90
===================================================================
--- LMDZ6/trunk/libf/phylmdiso/infotrac_phy.F90	(revision 4045)
+++ LMDZ6/trunk/libf/phylmdiso/infotrac_phy.F90	(revision 4046)
@@ -7,4 +7,6 @@
 ! the dynamics (could be further cleaned) and is initialized using values
 ! provided by the dynamics
+
+  USE readTracFiles_mod, ONLY: trac_type, maxlen, delPhase
 
 ! nqtot : total number of tracers and higher order of moment, water vapor and liquid included
@@ -41,12 +43,7 @@
 !$OMP THREADPRIVATE(nqperes)
 
-! Name variables
-  INTEGER,PARAMETER :: tname_lenmax=128 
-  CHARACTER(len=tname_lenmax), ALLOCATABLE, DIMENSION(:), SAVE :: tname ! tracer short name for restart and diagnostics
-  CHARACTER(len=tname_lenmax+3), ALLOCATABLE, DIMENSION(:), SAVE :: ttext ! tracer long name for diagnostics
-!$OMP THREADPRIVATE(tname,ttext)
-
-!! iadv  : index of trasport schema for each tracer
-!  INTEGER, ALLOCATABLE, DIMENSION(:), SAVE    :: iadv
+! Tracers parameters
+  TYPE(trac_type), TARGET, ALLOCATABLE, SAVE :: tracers(:)
+!$OMP THREADPRIVATE(tracers)
 
 ! niadv : vector keeping the coorspondance between all tracers(nqtot) treated in the 
@@ -107,5 +104,5 @@
 CONTAINS
 
-  SUBROUTINE init_infotrac_phy(nqtot_,nqo_,nbtr_,nqtottr_,nqCO2_,tname_,ttext_,type_trac_,&
+  SUBROUTINE init_infotrac_phy(nqtot_,nqo_,nbtr_,nqtottr_,nqCO2_,tracers_,type_trac_,&
                                niadv_,conv_flg_,pbl_flg_,solsym_,&
                                nqfils_,nqdesc_,nqdesc_tot_,iqfils_,iqpere_,&
@@ -139,6 +136,5 @@
     INTEGER,INTENT(IN) :: id_BIN01_strat_
 #endif
-    CHARACTER(len=*),INTENT(IN) :: tname_(nqtot_) ! tracer short name for restart and diagnostics
-    CHARACTER(len=*),INTENT(IN) :: ttext_(nqtot_) ! tracer long name for diagnostics
+    CHARACTER(len=*),INTENT(IN) :: tracers_(nqtot_) ! tracers descriptors
     CHARACTER(len=*),INTENT(IN) :: type_trac_
     INTEGER,INTENT(IN) :: niadv_ (nqtot_) ! equivalent dyn / physique
@@ -179,4 +175,5 @@
     nqCO2=nqCO2_
     nqtottr=nqtottr_
+    ALLOCATE(tracers(nqtot)); tracers(:) = tracers_(:)
 #ifdef CPP_StratAer
     nbtr_bin=nbtr_bin_
@@ -187,8 +184,4 @@
     id_BIN01_strat=id_BIN01_strat_
 #endif
-    ALLOCATE(tname(nqtot))
-    tname(:) = tname_(:)
-    ALLOCATE(ttext(nqtot))
-    ttext(:) = ttext_(:)
     type_trac = type_trac_
     ALLOCATE(niadv(nqtot))
Index: LMDZ6/trunk/libf/phylmdiso/phyetat0.F90
===================================================================
--- LMDZ6/trunk/libf/phylmdiso/phyetat0.F90	(revision 4045)
+++ LMDZ6/trunk/libf/phylmdiso/phyetat0.F90	(revision 4046)
@@ -31,5 +31,5 @@
   USE geometry_mod, ONLY : longitude_deg, latitude_deg
   USE iostart, ONLY : close_startphy, get_field, get_var, open_startphy
-  USE infotrac_phy, only: nbtr, nqo, type_trac, tname, niadv, &
+  USE infotrac_phy, only: nbtr, nqo, type_trac, tracers, niadv, &
         itr_indice ! C Risi
   USE traclmdz_mod,    ONLY : traclmdz_from_restart
@@ -476,6 +476,6 @@
         iq=itr_indice(it)                                                    
         iiq=niadv(iq)                                                        ! jyg
-        found=phyetat0_get(1,trs(:,it),"trs_"//tname(iiq), &
-              "Surf trac"//tname(iiq),0.)
+        found=phyetat0_get(1,trs(:,it),"trs_"//TRIM(tracers(iiq)%name), &
+                                  "Surf trac"//TRIM(tracers(iiq)%name),0.)
      ENDDO
      CALL traclmdz_from_restart(trs)
@@ -489,5 +489,5 @@
         found=phyetat0_get(1,co2_send,"co2_send","co2 send",co2_ppm)
      ENDIF
-  ENDIF !IF (type_trac == 'lmdz') THEN
+  ENDIF
 
 #ifdef ISO
Index: LMDZ6/trunk/libf/phylmdiso/phyredem.F90
===================================================================
--- LMDZ6/trunk/libf/phylmdiso/phyredem.F90	(revision 4045)
+++ LMDZ6/trunk/libf/phylmdiso/phyredem.F90	(revision 4046)
@@ -38,5 +38,5 @@
   USE iostart, ONLY: open_restartphy, close_restartphy, enddef_restartphy, put_field, put_var
   USE traclmdz_mod, ONLY : traclmdz_to_restart
-  USE infotrac_phy, ONLY: type_trac, niadv, tname, nbtr, nqo,itr_indice
+  USE infotrac_phy, ONLY: type_trac, niadv, tracers, nbtr, nqo,itr_indice
 #ifdef ISO
   USE infotrac_phy, ONLY: itr_indice,niso,ntraciso
@@ -350,5 +350,5 @@
           iq=itr_indice(it)                                                           ! jyg
           iiq=niadv(iq)                                                           ! jyg
-          CALL put_field(pass,"trs_"//tname(iiq), "", trs(:, it))
+          CALL put_field(pass,"trs_"//tracers(iiq)%name, "", trs(:, it))
        END DO
        IF (carbon_cycle_cpl) THEN
@@ -407,6 +407,4 @@
     IF (pass==2) CALL close_restartphy
   ENDDO ! DO pass=1,2   ! pass=1 netcdf definition ; pass=2 netcdf write
-  
-
   
   !$OMP BARRIER
Index: LMDZ6/trunk/libf/phylmdiso/phys_output_mod.F90
===================================================================
--- LMDZ6/trunk/libf/phylmdiso/phys_output_mod.F90	(revision 4045)
+++ LMDZ6/trunk/libf/phylmdiso/phys_output_mod.F90	(revision 4046)
@@ -35,6 +35,7 @@
     USE iophy 
     USE dimphy
-    USE infotrac_phy, ONLY: nqtot, nqo, niadv, tname, ttext, type_trac, &
+    USE infotrac_phy, ONLY: nqtot, nqo, niadv, tracers, type_trac, &
         nqtottr,itr_indice ! C Risi
+    USE strings_mod,  ONLY: maxlen
     USE ioipsl
     USE phys_cal_mod, only : hour, calend
@@ -124,8 +125,10 @@
 
 #ifdef ISO
-      INTEGER  :: ixt,iiso,izone
-      CHARACTER*50 :: striso_sortie
-      integer :: lnblnk
-#endif
+    INTEGER  :: ixt,iiso,izone
+    CHARACTER(LEN=50) :: outiso
+    CHARACTER(LEN=20) :: unit
+#endif
+    CHARACTER(LEN=maxlen) :: tnam, lnam, dn
+    INTEGER :: flag(nfiles)
 
 !!!!!!!!!! stockage dans une region limitee pour chaque fichier !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
@@ -534,92 +537,47 @@
         write(lunout,*) 'itr_indice=',itr_indice
 !       IF (nqtot>=nqo+1) THEN
-        IF (nqtottr>=1) THEN
+         IF (nqtottr>=1) THEN
 !
             !DO iq=nqo+1,nqtot 
             ! C Risi: on modifie la boucle
-            do itr=1,nqtottr ! C Risi
+          DO itr=1,nqtottr ! C Risi
             iq=itr_indice(itr)  ! C Risi
             write(*,*) 'phys_output_mid 503: itr=',itr
   
             iiq=niadv(iq)
-            o_trac(itr) = ctrl_out((/ 1, 5, 5, 5, 10, 10, 11, 11, 11, 11 /), &
-                           tname(iiq),'Tracer '//ttext(iiq), "-",  &
-                           (/ '', '', '', '', '', '', '', '', '', '' /))
-            o_dtr_vdf(itr) = ctrl_out((/ 4, 7, 7, 7, 10, 10, 11, 11, 11, 11 /), &
-                              'd'//trim(tname(iq))//'_vdf',  &
-                              'Tendance tracer '//ttext(iiq), "-" , &
-                              (/ '', '', '', '', '', '', '', '', '', '' /))
-
-            o_dtr_the(itr) = ctrl_out((/ 5, 7, 7, 7, 10, 10, 11, 11, 11, 11 /), &
-                              'd'//trim(tname(iq))//'_the', &
-                              'Tendance tracer '//ttext(iiq), "-", &
-                              (/ '', '', '', '', '', '', '', '', '', '' /))
-
-            o_dtr_con(itr) = ctrl_out((/ 5, 7, 7, 7, 10, 10, 11, 11, 11, 11 /), &
-                              'd'//trim(tname(iq))//'_con', &
-                              'Tendance tracer '//ttext(iiq), "-", &
-                              (/ '', '', '', '', '', '', '', '', '', '' /))
-
-            o_dtr_lessi_impa(itr) = ctrl_out((/ 7, 7, 7, 7, 10, 10, 11, 11, 11, 11 /), &
-                                     'd'//trim(tname(iq))//'_lessi_impa', &
-                                     'Tendance tracer '//ttext(iiq), "-", &
-                                     (/ '', '', '', '', '', '', '', '', '', '' /))
-
-            o_dtr_lessi_nucl(itr) = ctrl_out((/ 7, 7, 7, 7, 10, 10, 11, 11, 11, 11 /), &
-                                     'd'//trim(tname(iq))//'_lessi_nucl', &
-                                     'Tendance tracer '//ttext(iiq), "-", &
-                                     (/ '', '', '', '', '', '', '', '', '', '' /))
-
-            o_dtr_insc(itr) = ctrl_out((/ 7, 7, 7, 7, 10, 10, 11, 11, 11, 11 /), &
-                               'd'//trim(tname(iq))//'_insc', &
-                               'Tendance tracer '//ttext(iiq), "-", &
-                               (/ '', '', '', '', '', '', '', '', '', '' /))
-
-            o_dtr_bcscav(itr) = ctrl_out((/ 7, 7, 7, 7, 10, 10, 11, 11, 11, 11 /), &
-                                 'd'//trim(tname(iq))//'_bcscav', &
-                                 'Tendance tracer '//ttext(iiq), "-", &
-                                 (/ '', '', '', '', '', '', '', '', '', '' /))
-
-            o_dtr_evapls(itr) = ctrl_out((/ 7, 7, 7, 7, 10, 10, 11, 11, 11, 11 /), &
-                                 'd'//trim(tname(iq))//'_evapls', &
-                                 'Tendance tracer '//ttext(iiq), "-", &
-                                 (/ '', '', '', '', '', '', '', '', '', '' /))
-
-            o_dtr_ls(itr) = ctrl_out((/ 7, 7, 7, 7, 10, 10, 11, 11, 11, 11 /), &
-                             'd'//trim(tname(iq))//'_ls', &
-                             'Tendance tracer '//ttext(iiq), "-", &
-                             (/ '', '', '', '', '', '', '', '', '', '' /))
-
-            o_dtr_trsp(itr) = ctrl_out((/ 7, 7, 7, 7, 10, 10, 11, 11, 11, 11 /), &
-                               'd'//trim(tname(iq))//'_trsp', &
-                               'Tendance tracer '//ttext(iiq), "-", &
-                               (/ '', '', '', '', '', '', '', '', '', '' /))
-
-            o_dtr_sscav(itr) = ctrl_out((/ 7, 7, 7, 7, 10, 10, 11, 11, 11, 11 /), &
-                                'd'//trim(tname(iq))//'_sscav', &
-                                'Tendance tracer '//ttext(iiq), "-", &
-                                (/ '', '', '', '', '', '', '', '', '', '' /))
-
-            o_dtr_sat(itr) = ctrl_out((/ 7, 7, 7, 7, 10, 10, 11, 11, 11, 11 /), &
-                               'd'//trim(tname(iq))//'_sat', &
-                               'Tendance tracer '//ttext(iiq), "-", &
-                               (/ '', '', '', '', '', '', '', '', '', '' /))
-
-            o_dtr_uscav(itr) = ctrl_out((/ 7, 7, 7, 7, 10, 10, 11, 11, 11, 11 /), &
-                                'd'//trim(tname(iq))//'_uscav', &
-                                'Tendance tracer '//ttext(iiq), "-", &
-                                 (/ '', '', '', '', '', '', '', '', '', '' /))
-
-            o_dtr_dry(itr) = ctrl_out((/ 7, 7, 7, 7, 10, 10, 11, 11, 11, 11 /), &
-                              'cum'//'d'//trim(tname(iq))//'_dry', &
-                              'tracer tendency dry deposition'//ttext(iiq), "-", &
-                              (/ '', '', '', '', '', '', '', '', '', '' /))
-
-            o_trac_cum(itr) = ctrl_out((/ 1, 4, 10, 10, 10, 10, 11, 11, 11, 11 /), &
-                               'cum'//tname(iiq),&
-                               'Cumulated tracer '//ttext(iiq), "-", &
-                               (/ '', '', '', '', '', '', '', '', '', '' /))
-            ENDDO
-      ENDIF
+            dn = 'd'//TRIM(tracers(iiq)%name)//'_'
+
+            flag = [1, 5, 5, 5, 10, 10, 11, 11, 11, 11]
+            lnam = 'Tracer '//TRIM(tracers(iiq)%longName)
+            tnam = TRIM(tracers(iiq)%name); o_trac          (itr) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+
+            flag = [4, 7, 7, 7, 10, 10, 11, 11, 11, 11]
+            lnam = 'Tendance tracer '//TRIM(tracers(iiq)%longName)
+            tnam = TRIM(dn)//'vdf';         o_dtr_vdf       (itr) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+
+            flag = [5, 7, 7, 7, 10, 10, 11, 11, 11, 11]
+            tnam = TRIM(dn)//'the';         o_dtr_the       (itr) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+            tnam = TRIM(dn)//'con';         o_dtr_con       (itr) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+
+            flag = [7, 7, 7, 7, 10, 10, 11, 11, 11, 11]
+            tnam = TRIM(dn)//'lessi_impa';  o_dtr_lessi_impa(itr) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+            tnam = TRIM(dn)//'lessi_nucl';  o_dtr_lessi_nucl(itr) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+            tnam = TRIM(dn)//'insc';        o_dtr_insc      (itr) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+            tnam = TRIM(dn)//'bcscav';      o_dtr_bcscav    (itr) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+            tnam = TRIM(dn)//'evapls';      o_dtr_evapls    (itr) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+            tnam = TRIM(dn)//'ls';          o_dtr_ls        (itr) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+            tnam = TRIM(dn)//'trsp';        o_dtr_trsp      (itr) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+            tnam = TRIM(dn)//'sscav';       o_dtr_sscav     (itr) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+            tnam = TRIM(dn)//'sat';         o_dtr_sat       (itr) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+            tnam = TRIM(dn)//'uscav';       o_dtr_uscav     (itr) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+
+            lnam = 'tracer tendency dry deposition'//TRIM(tracers(iiq)%longName)
+            tnam = 'cum'//TRIM(dn)//'dry';  o_dtr_dry       (itr) = ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+
+            flag = [1, 4, 10, 10, 10, 10, 11, 11, 11, 11]
+            lnam = 'Cumulated tracer '//TRIM(tracers(iiq)%longName)
+            tnam = 'cum'//TRIM(tracers(iiq)%name); o_trac_cum(itr)= ctrl_out(flag, tnam, lnam, "-", [('',i=1,nfiles)])
+          ENDDO
+       ENDIF
 
    ENDDO !  iff
@@ -627,8 +585,8 @@
         write(*,*) 'phys_output_mid 589'
 #ifdef ISO
-  do ixt=1,ntraciso
-     if (ixt.le.niso) then
+    do ixt=1,ntraciso
+      if (ixt.le.niso) then
         striso_sortie=striso(ixt)
-     else
+      else
 #ifdef ISOTRAC
         iiso=index_iso(ixt)
@@ -639,70 +597,39 @@
         stop
 #endif
-     endif
-
-   o_xtprecip(ixt)=ctrl_out((/ 1, 1, 1, 10, 5, 10, 11, 11, 11, 11/), &
-    'precip'//striso_sortie(1:lnblnk(striso_sortie)),  &
-    'Precip Totale liq+sol', 'kg/(s*m2)', (/ ('', i=1, 10) /))   
-   o_xtplul(ixt) = ctrl_out((/ 1, 1, 1, 10, 10, 10, 11, 11, 11, 11/), &
-    'plul'//striso_sortie(1:lnblnk(striso_sortie)),  &
-    'Large-scale Precip.', 'kg/(s*m2)', (/ ('', i=1, 10) /))
-   o_xtpluc(ixt) = ctrl_out((/ 1, 1, 1, 10, 5, 10, 11, 11, 11, 11/), &
-    'pluc'//striso_sortie(1:lnblnk(striso_sortie)),  &
-    'Convective Precip.', 'kg/(s*m2)', (/ ('', i=1, 10) /))
-   o_xtevap(ixt) = ctrl_out((/ 1, 1, 10, 10, 10, 10, 11, 11, 11, 11/), &
-    'evap'//striso_sortie(1:lnblnk(striso_sortie)),  &
-    'Evaporat', 'kg/(s*m2)', (/ ('', i=1, 10) /))
-   o_xtovap(ixt) = ctrl_out((/ 2, 3, 4, 10, 10, 10, 11, 11, 11, 11/), &
-    'ovap'//striso_sortie(1:lnblnk(striso_sortie)),  &
-    'Specific humidity', 'kg/kg', (/ ('', i=1, 10) /))
-   o_xtoliq(ixt) = ctrl_out((/ 2, 3, 4, 10, 10, 10, 11, 11, 11, 11/), &
-    'oliq'//striso_sortie(1:lnblnk(striso_sortie)),  &
-    'Liquid water', 'kg/kg', (/ ('', i=1, 10) /))
-   o_xtcond(ixt) = ctrl_out((/ 2, 3, 4, 10, 10, 10, 11, 11, 11, 11/), &
-    'ocond'//striso_sortie(1:lnblnk(striso_sortie)),  &
-    'Condensed water', 'kg/kg', (/ ('', i=1, 10) /))     
-   o_dxtdyn(ixt) = ctrl_out((/ 4, 10, 10, 10, 10, 10, 11, 11, 11, 11/), &
-    'dqdyn'//striso_sortie(1:lnblnk(striso_sortie)),  &
-    'Dynamics dQ', '(kg/kg)/s', (/ ('', i=1, 10) /))
-   o_dxtldyn(ixt) = ctrl_out((/ 4, 10, 10, 10, 10, 10, 11, 11, 11, 11/), &
-    'dqldyn'//striso_sortie(1:lnblnk(striso_sortie)),  &
-    'Dynamics dQL', '(kg/kg)/s', (/ ('', i=1, 10) /))
-   o_dxtcon(ixt) = ctrl_out((/ 4, 10, 10, 10, 10, 10, 11, 11, 11, 11/), &
-    'dqcon'//striso_sortie(1:lnblnk(striso_sortie)),  &
-    'Convection dQ', '(kg/kg)/s', (/ ('', i=1, 10) /))
-   o_dxteva(ixt) = ctrl_out((/ 4, 10, 10, 10, 10, 10, 11, 11, 11, 11/), &
-    'dqeva'//striso_sortie(1:lnblnk(striso_sortie)),  &
-    'Reevaporation dQ', '(kg/kg)/s', (/ ('', i=1, 10) /))
-   o_dxtlsc(ixt) = ctrl_out((/ 4, 10, 10, 10, 10, 10, 11, 11, 11, 11/), &
-    'dqlsc'//striso_sortie(1:lnblnk(striso_sortie)),  &
-    'Condensation dQ', '(kg/kg)/s', (/ ('', i=1, 10) /))
-   o_dxtajs(ixt) = ctrl_out((/ 4, 10, 10, 10, 10, 10, 11, 11, 11, 11/), &
-    'dqajs'//striso_sortie(1:lnblnk(striso_sortie)),  &
-    'Dry adjust. dQ', '(kg/kg)/s', (/ ('', i=1, 10) /))
-   o_dxtvdf(ixt) = ctrl_out((/ 4, 10, 10, 10, 10, 10, 11, 11, 11, 11/), &
-    'dqvdf'//striso_sortie(1:lnblnk(striso_sortie)),  &
-    'Boundary-layer dQ', '(kg/kg)/s', (/ ('', i=1, 10) /))
-   o_dxtthe(ixt) = ctrl_out((/ 4, 10, 10, 10, 10, 10, 11, 11, 11, 11/), &
-    'dqthe'//striso_sortie(1:lnblnk(striso_sortie)),  &
-    'Thermal dQ', '(kg/kg)/s', (/ ('', i=1, 10) /))
-
-   IF (ok_qch4) then
-     o_dxtch4(ixt) = ctrl_out((/ 4, 10, 10, 10, 10, 10, 11, 11, 11, 11/), &
-        'dqch4'//striso_sortie(1:lnblnk(striso_sortie)),  &
-    'H2O due to CH4 oxidation & photolysis', '(kg/kg)/s', (/ ('', i=1, 10) /))
-   endif ! IF (ok_qch4) then
-
-   if (ixt.eq.iso_HTO) then
-      o_dxtprod_nucl(ixt) = ctrl_out((/ 4, 10, 10, 10, 10, 10, 11, 11, 11, 11/), &
-        'dqprodnucl'//striso_sortie(1:lnblnk(striso_sortie)),  &
-        'dHTO/dt due to nuclear production', '(kg/kg)/s', (/ ('', i=1, 10) /))
-      o_dxtcosmo(ixt) = ctrl_out((/ 4, 10, 10, 10, 10, 10, 11, 11, 11, 11/), &
-        'dqcosmo'//striso_sortie(1:lnblnk(striso_sortie)),  &
-        'dHTO/dt due to cosmogenic production', '(kg/kg)/s', (/ ('', i=1, 10) /))
-      o_dxtdecroiss(ixt) = ctrl_out((/ 4, 10, 10, 10, 10, 10, 11, 11, 11, 11/), &
-        'dqdecroiss'//striso_sortie(1:lnblnk(striso_sortie)),  &
-        'dHTO/dt due to radiative destruction', '(kg/kg)/s', (/ ('', i=1, 10) /))
-   endif !if (ixt.eq.iso_HTO) then
-  enddo !do ixt=1,niso
+      endif
+
+      flag = [1,  1,  1, 10,  5, 10, 11, 11, 11, 11]; unit = 'kg/(s*m2)'
+      o_xtprecip(ixt)=ctrl_out(flag, 'precip'//TRIM(outiso), 'Precip Totale liq+sol', unit, [('',i=1,nfiles)])
+      o_xtpluc  (ixt)=ctrl_out(flag,   'pluc'//TRIM(outiso),    'Convective Precip.', unit, [('',i=1,nfiles)])
+
+      flag = [1,  1,  1, 10, 10, 10, 11, 11, 11, 11]
+      o_xtplul  (ixt)=ctrl_out(flag,   'plul'//TRIM(outiso),   'Large-scale Precip.', unit, [('',i=1,nfiles)])
+      o_xtevap  (ixt)=ctrl_out(flag,   'evap'//TRIM(outiso),             'Evaporat.', unit, [('',i=1,nfiles)])
+
+      flag = [2,  3,  4, 10, 10, 10, 11, 11, 11, 11]; unit = 'kg/kg'
+      o_xtovap  (ixt)=ctrl_out(flag,   'ovap'//TRIM(outiso),     'Specific humidity', unit, [('',i=1,nfiles)])
+      o_xtoliq  (ixt)=ctrl_out(flag,   'oliq'//TRIM(outiso),          'Liquid water', unit, [('',i=1,nfiles)])
+      o_xtcond  (ixt)=ctrl_out(flag,  'ocond'//TRIM(outiso),       'Condensed water', unit, [('',i=1,nfiles)])
+
+      flag = [4, 10, 10, 10, 10, 10, 11, 11, 11, 11]; unit = '(kg/kg)/s'
+      o_dxtdyn  (ixt)=ctrl_out(flag,  'dqdyn'//TRIM(outiso),           'Dynamics dQ', unit, [('',i=1,nfiles)])
+      o_dxtldyn (ixt)=ctrl_out(flag, 'dqldyn'//TRIM(outiso),          'Dynamics dQL', unit, [('',i=1,nfiles)])
+      o_dxtcon  (ixt)=ctrl_out(flag,  'dqcon'//TRIM(outiso),         'Convection dQ', unit, [('',i=1,nfiles)])
+      o_dxteva  (ixt)=ctrl_out(flag,  'dqeva'//TRIM(outiso),      'Reevaporation dQ', unit, [('',i=1,nfiles)])
+      o_dxtlsc  (ixt)=ctrl_out(flag,  'dqlsc'//TRIM(outiso),       'Condensation dQ', unit, [('',i=1,nfiles)])
+      o_dxtajs  (ixt)=ctrl_out(flag,  'dqajs'//TRIM(outiso),        'Dry adjust. dQ', unit, [('',i=1,nfiles)])
+      o_dxtvdf  (ixt)=ctrl_out(flag,  'dqvdf'//TRIM(outiso),     'Boundary-layer dQ', unit, [('',i=1,nfiles)])
+      o_dxtthe  (ixt)=ctrl_out(flag,  'dqthe'//TRIM(outiso),            'Thermal dQ', unit, [('',i=1,nfiles)])
+
+      IF(ok_qch4) o_dxtch4(ixt)=ctrl_out(flag, 'dqch4'//TRIM(outiso), 'H2O due to CH4 oxidation & photolysis', &
+                                                                                      unit, [('',i=1,nfiles)])
+      IF(ixt == iso_HTO) THEN
+      o_dxtprod_nucl(ixt)=ctrl_out(flag, 'dqprodnucl'//TRIM(outiso), 'dHTO/dt due to nuclear production',      &
+                                                                                      unit, [('',i=1,nfiles)])
+      o_dxtcosmo    (ixt)=ctrl_out(flag,    'dqcosmo'//TRIM(outiso), 'dHTO/dt due to cosmogenic production',   &
+                                                                                      unit, [('',i=1,nfiles)])
+      o_dxtdecroiss (ixt)=ctrl_out(flag, 'dqdecroiss'//TRIM(outiso), 'dHTO/dt due to radiative destruction',   &
+      END IF
+    enddo !do ixt=1,niso
 #endif
         write(*,*) 'phys_output_mid 596'
Index: LMDZ6/trunk/libf/phylmdiso/phys_output_write_mod.F90
===================================================================
--- LMDZ6/trunk/libf/phylmdiso/phys_output_write_mod.F90	(revision 4045)
+++ LMDZ6/trunk/libf/phylmdiso/phys_output_write_mod.F90	(revision 4046)
@@ -25,5 +25,7 @@
 
     USE dimphy, ONLY: klon, klev, klevp1
-    USE infotrac_phy, ONLY: nbtr
+    USE infotrac_phy, ONLY: nbtr, nqtot, nqo, type_trac, tracers, niadv, &
+        nqtottr,itr_indice
+    USE strings_mod,  ONLY: maxlen
     USE mod_phys_lmdz_para, ONLY: is_north_pole_phy,is_south_pole_phy
     USE mod_grid_phy_lmdz, ONLY : nbp_lon, nbp_lat
@@ -383,6 +385,4 @@
     USE pbl_surface_mod, ONLY: snow
     USE indice_sol_mod, ONLY: nbsrf
-    USE infotrac_phy, ONLY: nqtot, nqo, type_trac, tname, niadv, &
-        nqtottr,itr_indice ! C Risi: ajout nqtottr
 #ifdef ISO
     USE infotrac_phy, only: ntraciso,niso,nqtottr
@@ -467,5 +467,5 @@
     INTEGER ISW
     CHARACTER*1 ch1
-    CHARACTER*20 varname
+    CHARACTER(LEN=maxlen) :: varname, dn
 
 #ifdef CPP_XIOS
@@ -538,47 +538,52 @@
           DO iq=nqo+1, nqtot
             iiq=niadv(iq)
-            varname=trim(tname(iiq))
-            WRITE (lunout,*) 'XIOS var=', nqo, iq, nqtot, varname
+            dn = 'd'//TRIM(tracers(iiq)%name)//'_'
+            WRITE (lunout,*) 'XIOS var=', nqo, iq, nqtot, tracers(iiq)%name
+
+            unt = "kg kg-1"
+            varname=trim(tracers(iiq)%name)
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg kg-1")
-            varname='d'//trim(tname(iiq))//'_vdf'
+            CALL xios_set_attr(child, name=varname, unit=unt)
+
+            unt = "kg kg-1 s-1"
+            varname=TRIM(dn)//'vdf'
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg kg-1 s-1")
-            varname='d'//trim(tname(iiq))//'_the'
+            CALL xios_set_attr(child, name=varname, unit=unt)
+            varname=TRIM(dn)//'the'
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg kg-1 s-1")
-            varname='d'//trim(tname(iiq))//'_con'
+            CALL xios_set_attr(child, name=varname, unit=unt)
+            varname=TRIM(dn)//'con'
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg kg-1 s-1")
-            varname='d'//trim(tname(iiq))//'_lessi_impa'
+            CALL xios_set_attr(child, name=varname, unit=unt)
+            varname=TRIM(dn)//'lessi_impa'
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg kg-1 s-1")
-            varname='d'//trim(tname(iiq))//'_lessi_nucl'
+            CALL xios_set_attr(child, name=varname, unit=unt)
+            varname=TRIM(dn)//'lessi_nucl'
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg kg-1 s-1")
-            varname='d'//trim(tname(iiq))//'_insc'
+            CALL xios_set_attr(child, name=varname, unit=unt)
+            varname=TRIM(dn)//'insc'
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg kg-1 s-1")
-            varname='d'//trim(tname(iiq))//'_bcscav'
+            CALL xios_set_attr(child, name=varname, unit=unt)
+            varname=TRIM(dn)//'bcscav'
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg kg-1 s-1")
-            varname='d'//trim(tname(iiq))//'_evapls'
+            CALL xios_set_attr(child, name=varname, unit=unt)
+            varname=TRIM(dn)//'evapls'
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg kg-1 s-1")
-            varname='d'//trim(tname(iiq))//'_ls'
+            CALL xios_set_attr(child, name=varname, unit=unt)
+            varname=TRIM(dn)//'ls'
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg kg-1 s-1")
-            varname='d'//trim(tname(iiq))//'_trsp'
+            CALL xios_set_attr(child, name=varname, unit=unt)
+            varname=TRIM(dn)//'trsp'
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg kg-1 s-1")
-            varname='d'//trim(tname(iiq))//'_sscav'
+            CALL xios_set_attr(child, name=varname, unit=unt)
+            varname=TRIM(dn)//'sscav'
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg kg-1 s-1")
-            varname='d'//trim(tname(iiq))//'_sat'
+            CALL xios_set_attr(child, name=varname, unit=unt)
+            varname=TRIM(dn)//'sat'
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg kg-1 s-1")
-            varname='d'//trim(tname(iiq))//'_uscav'
+            CALL xios_set_attr(child, name=varname, unit=unt)
+            varname=TRIM(dn)//'uscav'
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg kg-1 s-1")
+            CALL xios_set_attr(child, name=varname, unit=unt)
           ENDDO
           !On ajoute les variables 2D traceurs par l interface fortran 
@@ -587,11 +592,15 @@
           DO iq=nqo+1, nqtot
             iiq=niadv(iq)
-            varname='cum'//trim(tname(iiq))
+
+            unt = "kg m-2"
+            varname='cum'//trim(tracers(iiq)%name)
             WRITE (lunout,*) 'XIOS var=', iq, nqtot, varname
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg m-2")
-            varname='cumd'//trim(tname(iiq))//'_dry'
+            CALL xios_set_attr(child, name=varname, unit=unt)
+
+            unt = "kg m-2 s-1"
+            varname='cumd'//trim(tracers(iiq)%name)//'_dry'
             CALL xios_add_child(group_handle, child, varname)
-            CALL xios_set_attr(child, name=varname, unit="kg m-2 s-1")
+            CALL xios_set_attr(child, name=varname, unit=unt)
           ENDDO
     ENDIF
