source: LMDZ6/branches/LMDZ-tracers/libf/misc/readTracFiles_mod.f90 @ 4448

Last change on this file since 4448 was 3991, checked in by dcugnet, 3 years ago
  • fixed a bug in dynetat0[_loc].F90 for old style tracers description files having more water tracers than the initial state file.
  • changes (mainly cosmetic) to make dynetat0 and dynetat0_loc more similar.
  • fix a bug in readTracFiles_mod for tagging tracers.
File size: 84.9 KB
Line 
1MODULE readTracFiles_mod
2
3  USE strings_mod, ONLY: msg, testFile,  strFind, strStack, strReduce,  strHead, strCount,   find, dispTable, fmsg, &
4          removeComment, cat, checkList, strIdx,  strParse, strReplace, strTail, reduceExpr, test, modname, get_in
5  USE trac_types_mod, ONLY : tra, iso, db, kys
6
7  IMPLICIT NONE
8
9  PRIVATE
10
11  PUBLIC :: initIsotopes
12  PUBLIC :: readTracersFiles, aliasTracer, tracersSubset, indexUpdate     !--- TOOLS ASSOCIATED TO TRACERS  DESCRIPTORS
13  PUBLIC :: readIsotopesFile                                              !--- TOOLS ASSOCIATED TO ISOTOPES DESCRIPTORS
14  PUBLIC :: getKey_init, getKey, setDirectKeys                            !--- FUNCTIONS TO GET KEYS FROM tracers & isotopes
15
16  PUBLIC :: known_phases, old_phases, nphases, phases_names, phases_sep, &!--- VARIABLES RELATED TO THE PHASES
17            delPhase, addPhase                                            !--- ROUTINES TO ADD/REMOVE PHASE TO/FROM A NAME
18
19  PUBLIC :: tran0, idxAncestor, ancestor                                  !--- GEN 0 TRACER + TOOLS FOR GENERATIONS
20
21!------------------------------------------------------------------------------------------------------------------------------
22  INTERFACE getKey
23    MODULE PROCEDURE getKeyByName_s1, getKeyByName_i1, getKeyByName_r1, getKeyByName_sm, getKeyByName_im, getKeyByName_rm
24  END INTERFACE getKey
25!------------------------------------------------------------------------------------------------------------------------------
26  INTERFACE tracersSubset; MODULE PROCEDURE trSubset_Indx, trSubset_Name, trSubset_Nam1; END INTERFACE tracersSubset
27  INTERFACE idxAncestor;   MODULE PROCEDURE idxAncestor_1, idxAncestor_m; END INTERFACE idxAncestor
28  INTERFACE    ancestor;   MODULE PROCEDURE    ancestor_1,    ancestor_m; END INTERFACE    ancestor
29  INTERFACE    addPhase;   MODULE PROCEDURE    addPhase_1,    addPhase_m; END INTERFACE    addPhase
30!------------------------------------------------------------------------------------------------------------------------------
31
32  !=== MAIN DATABASE: files sections descriptors
33  TYPE(db), SAVE, ALLOCATABLE, TARGET :: dBase(:)
34
35  !--- SOME PARAMETERS THAT ARE NOT LIKELY TO CHANGE OFTEN
36  CHARACTER(LEN=256), SAVE      :: tran0        = 'air'              !--- Default transporting fluid
37  CHARACTER(LEN=256), PARAMETER :: old_phases   = 'vli'              !--- Old phases for water (no separator)
38  CHARACTER(LEN=256), PARAMETER :: known_phases = 'gls'              !--- Known phases initials
39  INTEGER,            PARAMETER :: nphases = LEN_TRIM(known_phases)  !--- Number of phases
40  CHARACTER(LEN=256), SAVE      :: phases_names(nphases) &           !--- Known phases names
41                                 = ['gaseous', 'liquid ', 'solid  ']
42  CHARACTER(LEN=1),   SAVE      :: phases_sep  =  '_'                !--- Phase separator
43  LOGICAL,            SAVE      :: tracs_merge = .TRUE.              !--- Merge/stack tracers lists
44  LOGICAL,            SAVE      :: lSortByGen  = .TRUE.              !--- Sort by growing generation
45
46  !=== LOCAL COPIES OF THE TRACERS AND ISOTOPES DESCRIPTORS, USED BY getKey (INITIALIZED IN getKey_init)
47  TYPE(tra), ALLOCATABLE, TARGET, SAVE ::  tracers(:)
48  TYPE(iso), ALLOCATABLE, TARGET, SAVE :: isotopes(:)
49
50
51CONTAINS
52
53!==============================================================================================================================
54!==============================================================================================================================
55!=== READ ONE OR SEVERAL TRACER FILES AND FILL A "tr" TRACERS DESCRIPTOR DERIVED TYPE.
56!=== THE RETURN VALUE fType DEPENDS ON WHAT IS FOUND:
57!===  0: NO ADEQUATE FILE FOUND ; DEFAULT VALUES MUST BE USED
58!===  1: AN "OLD STYLE" TRACERS FILE "traceur.def":
59!===    First line: <nb tracers>     Other lines: <hadv> <vadv> <tracer name> [<parent name>]
60!===  2: A  "NEW STYLE" TRACERS FILE  "tracer.def" WITH SEVERAL SECTIONS.
61!===  3: SEVERAL  "  "  TRACERS FILES "tracer_<component>.def" WITH A SINGLE SECTION IN EACH.
62!=== REMARKS:
63!===  * EACH SECTION BEGINS WITH A "&<section name> LINE
64!===  * DEFAULT VALUES FOR ALL THE SECTIONS OF THE FILE ARE DEFINED IN THE SPECIAL SECTION "&default"
65!===  * EACH SECTION LINE HAS THE STRUCTURE:  <name(s)>  <key1>=<value1> <key2>=<value2> ...
66!===  * SO FAR, THE DEFINED KEYS ARE: parent, phases, hadv, vadv, type
67!===  * <name> AND <parent> CAN BE LISTS OF COMA-SEPARATED TRACERS ; THE ROUTINE EXPAND THESE FACTORIZATIONS.
68!=== FUNCTION RETURN VALUE "lerr" IS FALSE IN CASE SOMETHING WENT WRONG.
69!=== ABOUT THE KEYS:
70!     * The "keys" component (of type kys) is in principle enough to store everything we could need.
71!     But some variables are stored as direct-access keys to make the code more readable and because they are used often.
72!     * Most of the direct-access keys are set in this module, but some are not (lnam, iadv and itr for example).
73!     * Some of the direct-access keys must be updated (using the routine "setDirectKeys") is a subset of "tracers(:)"
74!     is extracted: the indexes are no longer valid for a subset (examples: tracers(:)iprnt or tracers(:)%ichld).
75!     * If you need to convert a %key/%val pair into a direct-access key, add the corresponding line in "setDirectKeys".
76!==============================================================================================================================
77LOGICAL FUNCTION readTracersFiles(type_trac, fType, tracs) RESULT(lerr)
78!------------------------------------------------------------------------------------------------------------------------------
79  CHARACTER(LEN=*),       INTENT(IN)  :: type_trac                    !--- List of components used
80  INTEGER,                INTENT(OUT) :: fType                        !--- Type of input file found
81  TYPE(tra), ALLOCATABLE, INTENT(OUT) :: tracs(:)
82  CHARACTER(LEN=256),     ALLOCATABLE ::  s(:), sections(:), trac_files(:)
83  CHARACTER(LEN=256)   :: str, fname, mesg
84  INTEGER              :: is, nsec, ierr, it, ntrac, ns, ip
85  LOGICAL, ALLOCATABLE :: ll(:), lGen3(:)
86!------------------------------------------------------------------------------------------------------------------------------
87  lerr = .FALSE.
88!  modname = 'readTracersFiles'
89  IF(.NOT.ALLOCATED(dBase)) ALLOCATE(dBase(0))
90
91  !--- Required sections + corresponding files names (new style single section case)
92  IF(test(strParse(type_trac, ',', sections), lerr)) RETURN          !--- Parse "type_trac" list
93
94  nsec = SIZE(sections, DIM=1)
95  ALLOCATE(trac_files(nsec)); DO is=1, nsec; trac_files(is) = 'tracer_'//TRIM(sections(is))//'.def'; END DO
96
97  !--- LOOK AT AVAILABLE FILES
98  ll = .NOT.testFile(trac_files)
99  fType = 0
100  IF(.NOT.testFile('traceur.def') .AND. nsec==1) fType = 1           !--- OLD STYLE FILE
101  IF(.NOT.testFile('tracer.def'))                fType = 2           !--- NEW STYLE ; SINGLE  FILE, SEVERAL SECTIONS
102  IF(ALL(ll))                                    fType = 3           !--- NEW STYLE ; SEVERAL FILES, SINGLE SECTION USED
103  IF(ANY(ll) .AND. fType/=3) THEN                                    !--- MISSING FILES
104    IF(test(checkList(trac_files, .NOT.ll, 'Failed reading tracers description', 'files', 'missing'), lerr)) RETURN
105  END IF
106
107  !--- TELLS WHAT WAS IS ABOUT TO BE USED
108  IF( fmsg(fType==0, 'No adequate tracers description file(s) found ; default values will be used')) RETURN
109  CALL msg(fType==1, 'Trying to read old-style tracers description file "traceur.def"')
110  CALL msg(fType==2, 'Trying to read the new style multi-sections tracers description file "tracer.def"')
111  CALL msg(fType==3, 'Trying to read the new style single section tracers description files "tracer_*.def"')
112
113  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
114  IF(fType==1) THEN                                                  !=== OLD FORMAT "traceur.def"
115  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
116    !--- OPEN THE "traceur.def" FILE
117    OPEN(90, FILE="traceur.def", FORM='formatted', STATUS='old', IOSTAT=ierr)
118
119    !--- GET THE TRACERS NUMBER
120    READ(90,'(i3)',IOSTAT=ierr)ntrac                                 !--- Number of lines/tracers
121    IF(test(fmsg(ierr /= 0, 'Invalid format for "'//TRIM(fname)//'"'), lerr)) RETURN
122
123    !--- READ THE REMAINING LINES: <hadv> <vadv> <tracer> [<transporting fluid>]
124    ALLOCATE(tracs(ntrac))
125    DO it=1,ntrac                                                    !=== READ RAW DATA: loop on the line/tracer number
126      READ(90,'(a)',IOSTAT=ierr) str
127      IF(test(fmsg(ierr>0, 'Invalid format for "' //TRIM(fname)//'"'), lerr)) RETURN
128      IF(test(fmsg(ierr<0, 'Not enough lines in "'//TRIM(fname)//'"'), lerr)) RETURN
129      ll = strParse(str, ' ', s, n=ns)
130      tracs(it)%keys%key = ['hadv', 'vadv']
131      tracs(it)%keys%val = s(1:2)
132      CALL msg(ns == 3 .AND. it == 1, 'This file is for air tracers only')
133      CALL msg(ns == 4 .AND. it == 1, 'This files specifies the transporting fluid')
134      tracs(it)%name = s(3); tracs(it)%phas = known_phases(1:1)      !--- Default: name, gazeous phase "g"
135      DO ip = 1, nphases                                             !--- Deal with old water names
136        IF(s(3) /= 'H2O'//old_phases(ip:ip)) CYCLE
137        tracs(it)%phas = known_phases(ip:ip); tracs(it)%name = 'H2O'//phases_sep//TRIM(tracs(it)%phas)
138      END DO
139      tracs(it)%prnt = tran0                                         !--- Default transporting fluid: Air
140      IF(ns == 4) tracs(it)%prnt = s(4)                              !--- Transporting fluid name
141    END DO
142    CLOSE(90)
143
144    lGen3 = tracs%igen==3
145    CALL setGeneration(tracs)                                        !--- Determine tracs(:)%igen values
146    IF(test(checkTracers(tracs,      fname,fname),lerr)) RETURN      !--- Detect orphans and check phases
147    IF(test(checkUnique (tracs,lGen3,fname,fname),lerr)) RETURN      !--- Detect repeated tracers
148    CALL sortTracers  (tracs)                                        !--- Sort the tracers
149    CALL setDirectKeys(tracs)                                        !--- Set the directly accessible keys
150    tracs(:)%keys%name = tracs(:)%name                               !--- Copy tracers names in keys components
151    RETURN
152  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
153  END IF
154  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
155
156  !=== USING NEW FORMAT TRACERS DESCRIPTION FILES WITH POSSIBLY SEVERAL SECTIONS
157  CALL msg(nsec > 1 .AND.      tracs_merge, 'The multiple required sections will be MERGED.')
158  CALL msg(nsec > 1 .AND. .NOT.tracs_merge, 'The multiple required sections will be CUMULATED.')
159
160  !=== FEED THE DATABASE WITH THE RAW CONTENT OF THE FILE
161  SELECT CASE(fType)
162    CASE(2); lerr = feedDBase(["tracer.def"],[type_trac])            !--- Single file, coma-separated sections list
163    CASE(3); lerr = feedDBase(trac_files,     sections  )            !--- Multiple files, one section name each file
164  END SELECT
165  IF(lerr) RETURN
166  IF(     tracs_merge) lerr = mergeTracers(dBase, tracs)             !--- MERGE    THE COMPONENTS OF THE DATABASE
167  IF(.NOT.tracs_merge) lerr = cumulTracers(dBase, tracs)             !--- CUMULATE THE COMPONENTS OF THE DATABASE
168  IF(lerr) RETURN
169  CALL setDirectKeys(tracs)                                          !--- Set the directly accessible keys
170
171END FUNCTION readTracersFiles
172!==============================================================================================================================
173
174!==============================================================================================================================
175LOGICAL FUNCTION feedDBase(fnames, snames) RESULT(lerr)
176! Purpose: Read the sections "snames(is)" (coma-separated list) from each "fnames(is)"
177!   file and create the corresponding tracers set descriptors in the database "dBase":
178! * dBase(id)%name                : section name
179! * dBase(id)%trac(:)%name        : tracers names
180! * dBase(id)%trac(it)%keys%key(:): names  of keys associated to tracer dBase(id)%trac(it)%name
181! * dBase(id)%trac(it)%keys%val(:): values of keys associated to tracer dBase(id)%trac(it)%name
182!------------------------------------------------------------------------------------------------------------------------------
183  CHARACTER(LEN=*), INTENT(IN)  :: fnames(:)                         !--- Files names
184  CHARACTER(LEN=*), INTENT(IN)  :: snames(:)                         !--- Coma-deparated list of sections (one list each file)
185  INTEGER,  ALLOCATABLE :: ndb(:)                                    !--- Nuber of sections for each file
186  INTEGER,  ALLOCATABLE :: ixf(:)                                    !--- File index for each section of the expanded list
187  LOGICAL,  ALLOCATABLE :: lTg(:)                                    !--- Tagging tracers mask
188  CHARACTER(LEN=256)    :: fnm, snm
189  INTEGER               :: idb, i
190  LOGICAL :: ll
191!------------------------------------------------------------------------------------------------------------------------------
192
193  !=== READ THE REQUIRED SECTIONS
194  ll = strCount(snames, ',', ndb)                                    !--- Number of sections for each file
195  ALLOCATE(ixf(SUM(ndb)))
196  DO i=1, SIZE(fnames)
197    IF(test(readSections(fnames(i), snames(i), 'default'), lerr)) RETURN
198    ixf(1+SUM(ndb(1:i-1)):SUM(ndb(1:i))) = i                         !--- File index for each section of the expanded list
199  END DO
200  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
201  DO idb=1,SIZE(dBase)                                               !--- LOOP ON THE LOADED SECTIONS
202  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
203    fnm = fnames(ixf(idb)); snm = dBase(idb)%name
204    IF(test(expandSection(dBase(idb)%trac,   snm, fnm),lerr)) RETURN !--- EXPAND NAMES AND PARENTS LISTS
205    CALL setGeneration   (dBase(idb)%trac)                           !--- DETERMINE GENERATION NUMBER
206    IF(test(checkTracers (dBase(idb)%trac,   snm, fnm),lerr)) RETURN !--- CHECK ORPHANS AND PHASES
207    lTg = dBase(idb)%trac(:)%type == 'tag'                           !--- Flag for tagging tracers
208    IF(test(checkUnique  (dBase(idb)%trac,lTg,snm,fnm),lerr)) RETURN !--- CHECK TRACERS UNIQUENESS
209    CALL expandPhases    (dBase(idb)%trac)                           !--- EXPAND THE PHASES
210    CALL sortTracers     (dBase(idb)%trac)                           !--- SORT TRACERS
211  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
212  END DO
213  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
214
215  !=== DISPLAY BASIC INFORMATION
216  lerr = ANY([(dispTraSection('Expanded list for section "'//TRIM(dBase(idb)%name)//'"',dBase(idb)%name), idb=1, SIZE(dBase))])
217END FUNCTION feedDBase
218!------------------------------------------------------------------------------------------------------------------------------
219
220!------------------------------------------------------------------------------------------------------------------------------
221LOGICAL FUNCTION readSections(fnam,snam,defName) RESULT(lerr)
222!------------------------------------------------------------------------------------------------------------------------------
223  CHARACTER(LEN=*),           INTENT(IN) :: fnam                     !--- File name
224  CHARACTER(LEN=*),           INTENT(IN) :: snam                     !--- Coma-separated sections list
225  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: defName                  !--- Special section (default values) name
226!------------------------------------------------------------------------------------------------------------------------------
227  TYPE(db),           ALLOCATABLE :: tdb(:)
228  CHARACTER(LEN=256), ALLOCATABLE :: sec(:)
229  INTEGER,            ALLOCATABLE ::  ix(:)
230  INTEGER :: n0, idb, ndb, i, j
231  LOGICAL :: ll
232!------------------------------------------------------------------------------------------------------------------------------
233  n0 = SIZE(dBase) + 1                                               !--- Index for next entry in the database
234  CALL readSections_all()                                            !--- Read all the sections of file "fnam"
235  ndb= SIZE(dBase)                                                   !--- Current number of sections in the database
236  IF(PRESENT(defName)) THEN                                          !--- Add default values to all the tracers
237    DO idb=n0,ndb; CALL addDefault(dBase(idb)%trac, defName); END DO !--- and remove the virtual tracer "defName"
238  END IF
239  ll = strParse(snam, ',', keys = sec)                               !--- Requested sections names
240  ix = strIdx(dBase(:)%name, sec(:))                                 !--- Indexes of requested sections in database
241  IF(test(checkList(sec, ix == 0, 'In file "'//TRIM(fnam)//'"','section(s)', 'missing'), lerr)) RETURN
242  tdb = dBase(:); dBase = [tdb(1:n0-1),tdb(PACK(ix, MASK=ix/=0))]    !--- Keep requested sections only
243
244CONTAINS
245
246!------------------------------------------------------------------------------------------------------------------------------
247SUBROUTINE readSections_all()
248!------------------------------------------------------------------------------------------------------------------------------
249  CHARACTER(LEN=256), ALLOCATABLE ::  s(:), v(:)
250  TYPE(tra),          ALLOCATABLE :: tt(:)
251  TYPE(tra)           :: tmp
252  CHARACTER(LEN=1024) :: str
253  CHARACTER(LEN=256)  :: secn
254  INTEGER             :: ierr, n
255!------------------------------------------------------------------------------------------------------------------------------
256  IF(.NOT.ALLOCATED(dBase)) ALLOCATE(dBase(0))
257  OPEN(90, FILE=fnam, FORM='formatted', STATUS='old')
258  DO; READ(90,'(a)', IOSTAT=ierr)str
259    IF(ierr    /= 0 ) EXIT                                           !--- Finished: error or end of file
260    IF(str(1:1)=='#') CYCLE                                          !--- Skip comments lines
261    CALL removeComment(str)                                          !--- Skip comments at the end of a line
262    IF(str     == '') CYCLE                                          !--- Skip empty line (probably at the end of the file)
263    IF(str(1:1)=='&') THEN                                           !=== SECTION HEADER LINE
264      ndb  = SIZE(dBase)                                             !--- Number of sections so far
265      secn = str(2:LEN_TRIM(str))//' '                               !--- Current section name
266      IF(ANY(dBase(:)%name == secn)) CYCLE                           !--- Already known section
267      IF(secn(1:7) == 'version')     CYCLE                           !--- Skip the "version" special section
268      ndb = ndb + 1                                                  !--- Extend database
269      ALLOCATE(tdb(ndb))
270      tdb(1:ndb-1)  = dBase
271      tdb(ndb)%name = secn
272      ALLOCATE(tdb(ndb)%trac(0))
273      CALL MOVE_ALLOC(FROM=tdb, TO=dBase)
274    ELSE                                                             !=== TRACER LINE
275      ll = strParse(str,' ', keys = s, vals = v, n = n)              !--- Parse <key>=<val> pairs
276      tt = dBase(ndb)%trac(:)
277      tmp%name = s(1); tmp%comp=secn; tmp%keys = kys(s(1), s(2:n), v(2:n))
278      dBase(ndb)%trac = [tt(:), tmp]
279      DEALLOCATE(tt)
280!      dBase(ndb)%trac = [dBase(ndb)%trac(:), tra(name=s(1), comp=secn, keys=kys(s(1), s(2:n), v(2:n)))]
281    END IF
282  END DO
283  CLOSE(90)
284
285END SUBROUTINE readSections_all
286!------------------------------------------------------------------------------------------------------------------------------
287
288END FUNCTION readSections
289!==============================================================================================================================
290
291
292!==============================================================================================================================
293SUBROUTINE addDefault(t, defName)
294!------------------------------------------------------------------------------------------------------------------------------
295! Purpose: Add the keys from virtual tracer named "defName" (if any) and remove this virtual tracer.
296!------------------------------------------------------------------------------------------------------------------------------
297  TYPE(tra), ALLOCATABLE, TARGET, INTENT(INOUT) :: t(:)
298  CHARACTER(LEN=*),               INTENT(IN)    :: defName
299  INTEGER :: jd, it, k
300  TYPE(kys), POINTER :: ky
301  TYPE(tra), ALLOCATABLE :: tt(:)
302  jd = strIdx(t(:)%name, defName)
303  IF(jd == 0) RETURN
304  ky => t(jd)%keys
305  DO k = 1, SIZE(ky%key)                                             !--- Loop on the keys of the tracer named "defName"
306    CALL addKey_m(ky%key(k), ky%val(k), t(:)%keys)                   !--- Add key to all the tracers (no overwriting)
307  END DO
308  tt = [t(1:jd-1),t(jd+1:SIZE(t))]; CALL MOVE_ALLOC(FROM=tt, TO=t)   !--- Remove the virtual tracer named "defName"
309END SUBROUTINE addDefault
310!==============================================================================================================================
311
312!==============================================================================================================================
313SUBROUTINE subDefault(t, defName, lSubLocal)
314!------------------------------------------------------------------------------------------------------------------------------
315! Purpose: Substitute the keys from virtual tracer named "defName" (if any) and remove this virtual tracer.
316!          Substitute the keys locally (for the current tracer) if the flag "lSubLocal" is .TRUE.
317!------------------------------------------------------------------------------------------------------------------------------
318  TYPE(tra), ALLOCATABLE, TARGET, INTENT(INOUT) :: t(:)
319  CHARACTER(LEN=*),               INTENT(IN)    :: defName
320  LOGICAL,                        INTENT(IN)    :: lSubLocal
321  INTEGER :: i0, it, ik
322  TYPE(kys), POINTER     :: k0, ky
323  TYPE(tra), ALLOCATABLE :: tt(:)
324  i0 = strIdx(t(:)%name, defName)
325  IF(i0 == 0) RETURN
326  k0 => t(i0)%keys
327  DO it = 1, SIZE(t); IF(it == i0) CYCLE                             !--- Loop on the tracers
328    ky => t(it)%keys
329
330    !--- Substitute in the values of <key>=<val> pairs the keys defined in the virtual tracer "defName"
331    DO ik = 1, SIZE(k0%key); CALL strReplace(ky%val, k0%key(ik), k0%val(ik), .TRUE.); END DO
332
333    IF(.NOT.lSubLocal) CYCLE
334    !--- Substitute in the values of <key>=<val> pairs the keys defined locally (in the current tracer)
335    DO ik = 1, SIZE(ky%key); CALL strReplace(ky%val, ky%key(ik), ky%val(ik), .TRUE.); END DO
336  END DO
337  tt = [t(1:i0-1),t(i0+1:SIZE(t))]; CALL MOVE_ALLOC(FROM=tt, TO=t)   !--- Remove the virtual tracer named "defName"
338
339END SUBROUTINE subDefault
340!==============================================================================================================================
341
342
343!==============================================================================================================================
344LOGICAL FUNCTION expandSection(tr, sname, fname) RESULT(lerr)
345!------------------------------------------------------------------------------------------------------------------------------
346! Purpose: Expand tracers and parents lists in the tracers descriptor "tra".
347! Note:  * The following keys are expanded, so are accessible explicitely using "%" operator: "parent" "type".
348!        * Default values are provided for these keys because they are necessary.
349!------------------------------------------------------------------------------------------------------------------------------
350  TYPE(tra),     ALLOCATABLE, INTENT(INOUT) :: tr(:)                 !--- Tracer derived type vector
351  CHARACTER(LEN=*),           INTENT(IN)    :: sname
352  CHARACTER(LEN=*), OPTIONAL, INTENT(IN)    :: fname
353  TYPE(tra),          ALLOCATABLE :: ttr(:)
354  CHARACTER(LEN=256), ALLOCATABLE :: ta(:), pa(:)
355  CHARACTER(LEN=256) :: msg1
356  INTEGER :: it, nt, iq, nq, itr, ntr, ipr, npr, i
357  LOGICAL :: ll
358  lerr = .FALSE.
359  nt = SIZE(tr)
360  nq = 0
361  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
362  DO it = 1, nt    !=== GET TRACERS NB AFTER EXPANSION + NEEDED KEYS (name, parent, type)
363  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
364    !--- Extract useful keys: parent name, type, component name
365    tr(it)%prnt = fgetKey(it, 'parent', tr(:)%keys,  tran0  )
366    tr(it)%type = fgetKey(it, 'type'  , tr(:)%keys, 'tracer')
367    tr(it)%comp = sname
368
369    !--- Determine the number of tracers and parents ; coherence checking
370    ll = strCount(tr(it)%name, ',', ntr)
371    ll = strCount(tr(it)%prnt, ',', npr)
372
373    !--- Tagging tracers only can have multiple parents
374    IF(test(npr/=1 .AND. TRIM(tr(it)%type)/='tag', lerr)) THEN
375      msg1 = 'Check section "'//TRIM(sname)//'"'
376      IF(PRESENT(fname)) msg1=TRIM(msg1)//' in file "'//TRIM(fname)//'"'
377      CALL msg(TRIM(msg1)//': "'//TRIM(tr(it)%name)//'" has several parents but is not a tag'); RETURN
378    END IF
379    nq = nq + ntr*npr                 
380  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
381  END DO
382  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
383  CALL delKey(['parent','type  '], tr)
384
385  ALLOCATE(ttr(nq))
386  iq = 1
387  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
388  DO it = 1, nt                                                      !=== EXPAND TRACERS AND PARENTS NAMES LISTS
389  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
390    ll = strParse(tr(it)%name, ',', ta, n=ntr)                       !--- Number of tracers
391    ll = strParse(tr(it)%prnt, ',', pa, n=npr)                       !--- Number of parents
392    DO ipr=1,npr                                                     !--- Loop on parents list elts
393      DO itr=1,ntr                                                   !--- Loop on tracers list elts
394        i = iq+itr-1+(ipr-1)*ntr
395        ttr(i)%name = ta(itr); ttr(i)%prnt = pa(ipr)
396        ttr(i)%keys = kys(ta(itr), tr(it)%keys%key, tr(it)%keys%val)
397      END DO
398    END DO
399    ttr(iq:iq+ntr*npr-1)%type = tr(it)%type                          !--- Duplicating type
400    iq = iq + ntr*npr
401  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
402  END DO
403  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
404  DEALLOCATE(ta,pa)
405  CALL MOVE_ALLOC(FROM=ttr, TO=tr)
406
407END FUNCTION expandSection
408!==============================================================================================================================
409
410!==============================================================================================================================
411SUBROUTINE setGeneration(tr)
412!------------------------------------------------------------------------------------------------------------------------------
413! Purpose: Determine, for each tracer of "tr(:)":
414!   * the generation number
415!   * the first generation ancestor name
416!------------------------------------------------------------------------------------------------------------------------------
417! Arguments:
418  TYPE(tra), INTENT(INOUT) :: tr(:)                                  !--- Tracer derived type vector
419!------------------------------------------------------------------------------------------------------------------------------
420! Local variables:
421  INTEGER :: iq, nq, ig
422  LOGICAL,            ALLOCATABLE :: lg(:)
423  CHARACTER(LEN=256), ALLOCATABLE :: prn(:)
424!------------------------------------------------------------------------------------------------------------------------------
425  tr(:)%igen = 0                                                     !--- error if 0
426  nq = SIZE(tr, DIM=1)                                               !--- Number of tracers lines
427  lg = tr(:)%prnt == tran0                                           !--- First generation tracers flag
428  WHERE(lg) tr(:)%igen = 1                                           !--- First generation tracers
429
430  !=== Determine generation for each tracer
431  ig=0; prn = [tran0]
432  DO                                                                 !--- Update current generation flag
433    IF(ig/=0) prn = PACK( tr(:)%name, MASK=tr(:)%igen == ig)
434    lg(:) = [(ANY(prn(:) == tr(iq)%prnt), iq=1, nq)]                 !--- Current generation tracers flag
435    IF( ALL( .NOT. lg ) ) EXIT                                       !--- Empty current generation
436    ig = ig+1; WHERE(lg) tr(:)%igen = ig
437  END DO
438  tr(:)%nam1 = ancestor(tr)                                          !--- First generation ancestor name
439
440END SUBROUTINE setGeneration
441!==============================================================================================================================
442
443!==============================================================================================================================
444LOGICAL FUNCTION checkTracers(tr, sname, fname) RESULT(lerr)
445!------------------------------------------------------------------------------------------------------------------------------
446! Purpose:
447!   * check for orphan tracers (without known parent)
448!   * check wether the phases are known or not ("g"aseous, "l"iquid or "s"olid so far)
449!------------------------------------------------------------------------------------------------------------------------------
450  TYPE(tra),                  INTENT(IN) :: tr(:)                    !--- Tracer derived type vector
451  CHARACTER(LEN=*),           INTENT(IN) :: sname                    !--- Section name
452  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: fname                    !--- File name
453  CHARACTER(LEN=256) :: mesg
454  CHARACTER(LEN=256) :: bp(SIZE(tr, DIM=1)), pha                     !--- Bad phases list, phases of current tracer
455  CHARACTER(LEN=1) :: p
456  INTEGER :: ip, np, iq, nq
457!------------------------------------------------------------------------------------------------------------------------------
458  nq = SIZE(tr,DIM=1)                                                !--- Number of tracers lines
459  mesg = 'Check section "'//TRIM(sname)//'"'
460  IF(PRESENT(fname)) mesg=TRIM(mesg)//' in file "'//TRIM(fname)//'"'
461
462  !=== CHECK FOR ORPHAN TRACERS
463  IF(test(checkList(tr%name, tr%igen==0, mesg, 'tracers', 'orphan'), lerr)) RETURN
464
465  !=== CHECK PHASES
466  DO iq=1,nq; IF(tr(iq)%igen/=1) CYCLE                               !--- Generation 1 only is checked
467    pha = fgetKey(iq, 'phases', tr(:)%keys, 'g')                     !--- Phases
468    np = LEN_TRIM(pha); bp(iq)=' '
469    DO ip=1,np; p = pha(ip:ip); IF(INDEX(known_phases,p)==0) bp(iq) = TRIM(bp(iq))//p; END DO
470    IF(TRIM(bp(iq)) /= '') bp(iq) = TRIM(tr(iq)%name)//': '//TRIM(bp(iq))
471  END DO
472  lerr = checkList(bp, tr%igen==1 .AND. bp/='', mesg, 'tracers phases', 'unknown')
473END FUNCTION checkTracers
474!==============================================================================================================================
475
476!==============================================================================================================================
477LOGICAL FUNCTION checkUnique(tr, lTag, sname, fname) RESULT(lerr)
478!------------------------------------------------------------------------------------------------------------------------------
479! Purpose: Make sure that tracers are not repeated.
480!------------------------------------------------------------------------------------------------------------------------------
481  TYPE(tra),                  INTENT(IN) :: tr(:)                    !--- Tracer derived type vector
482  LOGICAL,                    INTENT(IN) :: lTag(:)                  !--- Tagging tracer flag
483  CHARACTER(LEN=*),           INTENT(IN) :: sname                    !--- Section name
484  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: fname                    !--- File name
485!------------------------------------------------------------------------------------------------------------------------------
486  INTEGER :: ip, np, iq, nq, k
487  LOGICAL, ALLOCATABLE :: ll(:)
488  CHARACTER(LEN=256) :: mesg, tnam, tdup(SIZE(tr,DIM=1))
489  CHARACTER(LEN=1)   :: p
490!------------------------------------------------------------------------------------------------------------------------------
491  mesg = 'Check section "'//TRIM(sname)//'"'
492  IF(PRESENT(fname)) mesg=TRIM(mesg)//' in file "'//TRIM(fname)//'"'
493  nq=SIZE(tr,DIM=1); lerr=.FALSE.                                    !--- Number of lines ; error flag
494  tdup(:) = ''
495  DO iq=1,nq; IF(lTag(iq)) CYCLE                                     !--- Tags can be repeated
496    tnam = tr(iq)%name
497    ll = tr(:)%name==tnam                                            !--- Mask for current tracer name
498    IF(COUNT(ll)==1 ) CYCLE                                          !--- Tracer is not repeated
499    IF(tr(iq)%igen>1) THEN
500      tdup(iq) = tnam                                                !--- gen>1: MUST be unique
501    ELSE
502      DO ip=1,nphases; p=known_phases(ip:ip)                         !--- Loop on known phases
503        !--- Number of appearances of the current tracer with known phase "p"
504        np = COUNT( PACK( [(INDEX(fgetKey(k, 'phases', tr(:)%keys, 'g'),p), k=1, nq)] /=0 , MASK=ll ) )
505        IF(np <=1) CYCLE
506        tdup(iq) = TRIM(tdup(iq))//TRIM(phases_names(ip))
507        IF(ANY(tdup(1:iq-1) == tdup(iq))) tdup(iq)=''                !--- Avoid repeating same messages
508      END DO
509    END IF
510    IF(tdup(iq) /= '') tdup(iq)=TRIM(tnam)//' in '//TRIM(tdup(iq))//' phase(s)'
511  END DO
512  lerr = checkList(tdup, tdup/='', mesg, 'tracers', 'duplicated')
513END FUNCTION checkUnique
514!==============================================================================================================================
515
516!==============================================================================================================================
517SUBROUTINE expandPhases(tr)
518!------------------------------------------------------------------------------------------------------------------------------
519! Purpose: Expand the phases in the tracers descriptor "tr".
520!------------------------------------------------------------------------------------------------------------------------------
521  TYPE(tra), ALLOCATABLE, INTENT(INOUT) :: tr(:)                     !--- Tracer derived type vector
522!------------------------------------------------------------------------------------------------------------------------------
523  TYPE(tra), ALLOCATABLE :: ttr(:)
524  INTEGER,  ALLOCATABLE ::  i0(:)
525  CHARACTER(LEN=256)    :: nam, pha, trn
526  INTEGER :: ip, np, iq, jq, nq, it, nt, nc, i, n
527  LOGICAL :: lTg, lEx
528!------------------------------------------------------------------------------------------------------------------------------
529  nq = SIZE(tr, DIM=1)
530  nt = 0
531  DO iq = 1, nq                                                      !--- GET THE NUMBER OF TRACERS
532    IF(tr(iq)%igen /= 1) CYCLE
533    nc = COUNT(tr(:)%nam1==tr(iq)%name .AND. tr%igen/=1)             !--- Number of childs of tr(iq)
534    tr(iq)%phas = fgetKey(iq, 'phases', tr(:)%keys)                  !--- Phases list      of tr(iq)
535    np = LEN_TRIM(tr(iq)%phas)                                       !--- Number of phases of tr(iq)
536    nt = nt + (1+nc) * np                                            !--- Number of tracers after expansion
537  END DO
538  ALLOCATE(ttr(nt))
539  it = 1                                                             !--- Current "ttr(:)" index
540  DO iq = 1, nq                                                      !--- Loop on "tr(:)" indexes
541    lTg = tr(iq)%type=='tag'                                         !--- Current tracer is a tag
542    i0 = strFind(tr(:)%name, tr(iq)%nam1, n)                         !--- Indexes of first generation ancestor copies
543    np = SUM( [( LEN_TRIM(tr(i0(i))%phas),i=1,n )],1)                !--- Number of phases for current tracer tr(iq)
544    lEx = np>1                                                       !--- Need of a phase suffix
545    IF(lTg) lEx=lEx.AND.tr(iq)%igen>1                                !--- No phase suffix for first generation tags
546    DO i=1,n                                                         !=== LOOP ON FIRST GENERATION ANCESTORS
547      jq=i0(i)                                                       !--- tr(jq): ith copy of 1st gen. ancestor of tr(iq)
548      IF(tr(iq)%igen==1) jq=iq                                       !--- Generation 1: current tracer phases only
549      pha = tr(jq)%phas                                              !--- Phases list for tr(jq)
550      DO ip=1,LEN_TRIM(pha)                                          !=== LOOP ON PHASES LISTS
551        trn=TRIM(tr(iq)%name); nam=trn                               !--- Tracer name (regular case)
552        IF(lTg) nam = TRIM(tr(iq)%prnt)                              !--- Parent name (tagging case)
553        IF(lEx) nam = TRIM(nam)//phases_sep//pha(ip:ip)              !--- Phase extension needed
554        IF(lTg) nam = TRIM(nam)//'_'//TRIM(trn)                      !--- <parent>_<name> for tags
555        ttr(it) = tr(iq)                                             !--- Same <key>=<val> pairs
556        ttr(it)%name = nam                                           !--- Name with possibly phase suffix
557        ttr(it)%keys%name = nam                                      !--- Name inside the keys decriptor
558        ttr(it)%phas = pha(ip:ip)                                    !--- Single phase entry
559        IF(lEx.AND.tr(iq)%igen>1) THEN
560          ttr(it)%prnt = TRIM(ttr(it)%prnt)//phases_sep//pha(ip:ip)
561          ttr(it)%nam1 = TRIM(ttr(it)%nam1)//phases_sep//pha(ip:ip)
562        END IF
563        it=it+1
564      END DO
565      IF(tr(iq)%igen==1) EXIT                                        !--- Break phase loop for gen 1
566    END DO
567  END DO
568  CALL MOVE_ALLOC(FROM=ttr, TO=tr)
569  CALL delKey(['phases'],tr)                                         !--- Remove few keys entries
570
571END SUBROUTINE expandPhases
572!==============================================================================================================================
573
574!==============================================================================================================================
575SUBROUTINE sortTracers(tr)
576!------------------------------------------------------------------------------------------------------------------------------
577! Purpose: Sort tracers:
578!  * lGrowGen == T: in ascending generations numbers.
579!  * lGrowGen == F: tracer + its childs sorted by growing generation, one after the other.
580!------------------------------------------------------------------------------------------------------------------------------
581  TYPE(tra), ALLOCATABLE, INTENT(INOUT) :: tr(:)                     !--- Tracer derived type vector
582  INTEGER :: ig, ng, iq, jq, n, ix(SIZE(tr)), k
583  INTEGER, ALLOCATABLE :: iy(:), iz(:)
584!------------------------------------------------------------------------------------------------------------------------------
585  iq = 1
586  IF(lSortByGen) THEN
587    ng = MAXVAL(tr(:)%igen, MASK=.TRUE., DIM=1)                      !--- Number of generations
588    DO ig = 0, ng                                                    !--- Loop on generations
589      iy = PACK([(k, k=1, SIZE(tr))], MASK=tr(:)%igen==ig)           !--- Generation ig tracers indexes
590      n = SIZE(iy)
591      ix(iq:iq+n-1) = iy                                             !--- Stack growing generations idxs
592      iq = iq + n
593    END DO
594  ELSE
595    DO jq = 1, SIZE(tr,DIM=1)                                        !--- Loop on first generation tracers
596      IF(tr(jq)%igen /= 1) CYCLE                                     !--- Skip generations >= 1
597      ix(iq) = jq                                                    !--- First generation ancestor index first
598      iq = iq + 1
599      iy = strFind(tr(:)%nam1, tr(jq)%name)                          !--- Indexes of "tr(jq)" childs in "tr(:)"
600      ng = MAXVAL(tr(iy)%igen, MASK=.TRUE., DIM=1)                   !--- Generations number of the "tr(jq)" family
601      DO ig = 2, ng                                                  !--- Loop   on generations for the tr(jq) family
602        iz = find(tr(iy)%igen, ig, n)                                !--- Indexes of the tracers "tr(iy(:))" of generation "ig"
603        ix(iq:iq+n-1) = iy(iz)                                       !--- Same indexes in "tr(:)"
604        iq = iq + n
605      END DO
606    END DO
607  END IF
608  tr = tr(ix)                                                        !--- Reorder the tracers
609END SUBROUTINE sortTracers
610!==============================================================================================================================
611
612!==============================================================================================================================
613LOGICAL FUNCTION mergeTracers(sections, tr) RESULT(lerr)
614  TYPE(db),  TARGET,      INTENT(IN)  :: sections(:)
615  TYPE(tra), ALLOCATABLE, INTENT(OUT) ::       tr(:)
616  TYPE(tra), POINTER     ::   t1(:),   t2(:)
617  INTEGER,  ALLOCATABLE :: ixct(:), ixck(:)
618  INTEGER :: is, k1, k2, nk2, i1, i2, nt2
619  CHARACTER(LEN=256) :: s1, v1, v2, tnam, knam
620  lerr = .FALSE.
621  t1 => sections(1)%trac(:)                                          !--- Alias: first tracers section
622  tr = t1
623  !----------------------------------------------------------------------------------------------------------------------------
624  DO is=2,SIZE(sections)                                             !=== SEVERAL SECTIONS: MERGE THEM
625  !----------------------------------------------------------------------------------------------------------------------------
626    t2  => sections(is)%trac(:)                                      !--- Alias: current tracers section
627    nt2  = SIZE(t2(:), DIM=1)                                        !--- Number of tracers in section
628    ixct = strIdx(t1(:)%name, t2(:)%name)                            !--- Indexes of common tracers
629    tr = [tr, PACK(t2, MASK= ixct==0)]                               !--- Append with new tracers
630    IF( ALL(ixct == 0) ) CYCLE                                       !--- No common tracers => done
631    CALL msg('Tracers defined in previous sections and duplicated in "'//TRIM(sections(is)%name)//'":')
632    CALL msg( t1(PACK(ixct, MASK = ixct/=0))%name, nmax=128 )        !--- Display duplicates (the 128 first at most)
633    !--------------------------------------------------------------------------------------------------------------------------
634    DO i2=1,nt2; tnam = t2(i2)%name                                  !=== LOOP ON COMMON TRACERS
635    !--------------------------------------------------------------------------------------------------------------------------
636      i1 = ixct(i2); IF(i1 == 0) CYCLE                               !--- Idx in t1(:) ; skip new tracers
637
638      !=== CHECK WETHER ESSENTIAL KEYS ARE IDENTICAL OR NOT
639      s1=' of "'//TRIM(tnam)//'" in "'//TRIM(sections(is)%name)//'" not matching previous value'
640     
641      IF(test(fmsg(t1(i1)%prnt /= t2(i2)%prnt, 'Parent name'//TRIM(s1)), lerr)) RETURN
642      IF(test(fmsg(t1(i1)%type /= t2(i2)%type, 'Type'       //TRIM(s1)), lerr)) RETURN
643      IF(test(fmsg(t1(i1)%igen /= t2(i2)%igen, 'Generation' //TRIM(s1)), lerr)) RETURN
644
645      !=== APPEND <key>=<val> PAIRS NOT PREVIOULSLY DEFINED
646      nk2  = SIZE(t2(i2)%keys%key(:))                                !--- Keys number in current section
647      ixck = strIdx(t1(i1)%keys%key(:), t2(i2)%keys%key(:))          !--- Common keys indexes
648
649      !=== APPEND NEW KEYS
650      tr(i1)%keys%key = [ tr(i1)%keys%key, PACK(tr(i2)%keys%key, MASK = ixck==0)]
651      tr(i1)%keys%val = [ tr(i1)%keys%val, PACK(tr(i2)%keys%val, MASK = ixck==0)]
652
653      !--- KEEP TRACK OF THE COMPONENTS NAMES
654      tr(i1)%comp = TRIM(tr(i1)%comp)//','//TRIM(tr(i2)%comp)
655
656      !--- SELECT COMMON TRACERS WITH DIFFERING KEYS VALUES (PREVIOUS VALUE IS KEPT)
657      DO k2=1,nk2
658        k1 = ixck(k2); IF(k1 == 0) CYCLE
659        IF(t1(i1)%keys%val(k1) == t2(i2)%keys%val(k2)) ixck(k2)=0
660      END DO
661      IF(ALL(ixck==0)) CYCLE                                         !--- No identical keys with /=values
662
663      !--- DISPLAY INFORMATION: OLD VALUES ARE KEPT FOR THE KEYS FOUND IN PREVIOUS AND CURRENT SECTIONS
664      CALL msg('Key(s)'//TRIM(s1))
665      DO k2 = 1, nk2                                                 !--- Loop on keys found in both t1(:) and t2(:)
666        knam = t2(i2)%keys%key(k2)                                   !--- Name of the current key
667        k1 = ixck(k2)                                                !--- Corresponding index in t1(:)
668        IF(k1 == 0) CYCLE                                            !--- New keys are skipped
669        v1 = t1(i1)%keys%val(k1); v2 = t2(i2)%keys%val(k2)           !--- Key values in t1(:) and t2(:)
670        CALL msg(' * '//TRIM(knam)//'='//TRIM(v2)//' ; previous value kept:'//TRIM(v1))
671      END DO
672      !------------------------------------------------------------------------------------------------------------------------
673    END DO
674    !--------------------------------------------------------------------------------------------------------------------------
675  END DO
676  CALL sortTracers(tr)
677
678END FUNCTION mergeTracers
679!==============================================================================================================================
680
681!==============================================================================================================================
682LOGICAL FUNCTION cumulTracers(sections, tr) RESULT(lerr)
683  TYPE(db),  TARGET,      INTENT(IN)  :: sections(:)
684  TYPE(tra), ALLOCATABLE, INTENT(OUT) ::       tr(:)
685  TYPE(tra), POINTER     :: t1(:), t2(:)
686  INTEGER,   ALLOCATABLE :: nt(:)
687  CHARACTER(LEN=256)     :: tnam, tnam_new
688  INTEGER :: iq, nq, is, ns, nsec
689  lerr = .FALSE.                                                     !--- Can't fail ; kept to match "mergeTracer" interface.
690  nsec =  SIZE(sections)
691  tr = [(      sections(is)%trac(:) , is=1, nsec )]                  !--- Concatenated tracers vector
692  nt = [( SIZE(sections(is)%trac(:)), is=1, nsec )]                  !--- Number of tracers in each section
693  !----------------------------------------------------------------------------------------------------------------------------
694  DO is=1, nsec                                                      !=== LOOP ON SECTIONS
695  !----------------------------------------------------------------------------------------------------------------------------
696    t1 => sections(is)%trac(:)
697    !--------------------------------------------------------------------------------------------------------------------------
698    DO iq=1, nt(is)                                                  !=== LOOP ON TRACERS
699    !--------------------------------------------------------------------------------------------------------------------------
700      tnam = t1(iq)%name                                             !--- Original name
701      IF(COUNT(t1%name == tnam) == 1) CYCLE                          !--- Current tracer is not duplicated: finished
702      tnam_new = TRIM(tnam)//phases_sep//TRIM(sections(is)%name)     !--- Same with section extension
703      nq = SUM(nt(1:is-1))                                           !--- Number of tracers in previous sections
704      ns = nt(is)                                                    !--- Number of tracers in the current section
705      tr(iq + nq)%name = tnam_new                                    !--- Modify tracer name
706      WHERE(tr(1+nq:ns+nq)%prnt==tnam) tr(1+nq:ns+nq)%prnt=tnam_new  !--- Modify parent name
707    !--------------------------------------------------------------------------------------------------------------------------
708    END DO
709  !----------------------------------------------------------------------------------------------------------------------------
710  END DO
711  !----------------------------------------------------------------------------------------------------------------------------
712  CALL sortTracers(tr)
713END FUNCTION cumulTracers
714!==============================================================================================================================
715
716!==============================================================================================================================
717SUBROUTINE setDirectKeys(tr)
718  TYPE(tra), INTENT(INOUT) :: tr(:)
719  CALL indexUpdate(tr)                                               !--- Update iparnt and idesc indexes vectors
720!  DO iq = 1, SIZE(tr)
721!    tr(iq)%keys%<key> = getKey_prv(it, "<key>", tr%keys,  tran0 )   !--- For additional keys
722!  END DO
723END SUBROUTINE setDirectKeys
724!==============================================================================================================================
725
726!==============================================================================================================================
727LOGICAL FUNCTION dispTraSection(message, sname) RESULT(lerr)
728  CHARACTER(LEN=*), INTENT(IN) :: message
729  CHARACTER(LEN=*), INTENT(IN) :: sname
730  INTEGER :: idb, iq, nq
731  INTEGER, ALLOCATABLE :: hadv(:), vadv(:)
732  TYPE(tra), POINTER :: tm(:)
733  lerr = .FALSE.
734  idb = strIdx(dBase(:)%name, sname); IF(idb == 0) RETURN
735  tm => dBase(idb)%trac
736  nq = SIZE(tm)
737  IF(test(getKeyByName_im('hadv', hadv, tm(:)%name, tm(:)%keys),lerr)) RETURN
738  IF(test(getKeyByName_im('vadv', vadv, tm(:)%name, tm(:)%keys),lerr)) RETURN
739  CALL msg(TRIM(message)//':')
740  IF(test(dispTable('iiissis', ['iq        ','hadv      ','vadv      ','short name','parent    ','igen      ','phase     '], &
741                    cat(tm(:)%name,  tm(:)%prnt, tm(:)%phas), cat([(iq, iq=1, nq)],  hadv,  vadv, tm(:)%igen)), lerr)) RETURN
742END FUNCTION dispTraSection
743!==============================================================================================================================
744!==============================================================================================================================
745
746
747!==============================================================================================================================
748!== CREATE A SCALAR ALIAS OF THE COMPONENT OF THE TRACERS DESCRIPTOR "t" NAMED "tname" ========================================
749!==============================================================================================================================
750FUNCTION aliasTracer(tname, t) RESULT(out)
751  TYPE(tra),         POINTER    :: out
752  CHARACTER(LEN=*),  INTENT(IN) :: tname
753  TYPE(tra), TARGET, INTENT(IN) :: t(:)
754  INTEGER :: it
755  it = strIdx(t(:)%name, tname)
756  out => NULL(); IF(it /= 0) out => t(it)
757END FUNCTION aliasTracer
758!------------------------------------------------------------------------------------------------------------------------------
759
760
761!==============================================================================================================================
762!=== FROM A LIST OF INDEXES OR NAMES, CREATE A SUBSET OF THE TRACERS DESCRIPTORS LIST "trac" ==================================
763!==============================================================================================================================
764FUNCTION trSubset_Indx(trac,idx) RESULT(out)
765  TYPE(tra), ALLOCATABLE             ::  out(:)
766  TYPE(tra), ALLOCATABLE, INTENT(IN) :: trac(:)
767  INTEGER,                INTENT(IN) ::  idx(:)
768  out = trac(idx)
769  CALL indexUpdate(out)
770END FUNCTION trSubset_Indx
771!------------------------------------------------------------------------------------------------------------------------------
772FUNCTION trSubset_Name(trac,nam) RESULT(out)
773  TYPE(tra), ALLOCATABLE             ::  out(:)
774  TYPE(tra), ALLOCATABLE, INTENT(IN) :: trac(:)
775  CHARACTER(LEN=*),       INTENT(IN) ::  nam(:)
776  out = trac(strIdx(trac(:)%name, nam))
777  CALL indexUpdate(out)
778END FUNCTION trSubset_Name
779!------------------------------------------------------------------------------------------------------------------------------
780
781
782!==============================================================================================================================
783!=== CREATE THE SUBSET OF THE TRACERS DESCRIPTORS LIST "trac" HAVING THE FIRST GENERATION ANCESTOR NAMED "nam" ================
784!==============================================================================================================================
785FUNCTION trSubset_Nam1(trac,nam) RESULT(out)
786  TYPE(tra), ALLOCATABLE             ::  out(:)
787  TYPE(tra), ALLOCATABLE, INTENT(IN) :: trac(:)
788  CHARACTER(LEN=*),       INTENT(IN) ::  nam
789  out = trac(strFind(delPhase(trac(:)%nam1), nam))
790  CALL indexUpdate(out)
791END FUNCTION trSubset_Nam1
792!------------------------------------------------------------------------------------------------------------------------------
793
794
795!==============================================================================================================================
796!=== UPDATE THE INDEXES iparnt(:), idesc=(:) AND igen(:) IN THE TRACERS DESCRIPTOR LIST "tr" (USEFULL FOR SUBSETS) ============
797!==============================================================================================================================
798SUBROUTINE indexUpdate(tr)
799  TYPE(tra), INTENT(INOUT) :: tr(:)
800  INTEGER :: iq, ig, ng, ngen
801  INTEGER, ALLOCATABLE :: ix(:)
802  tr(:)%iprnt = strIdx( tr(:)%name, tr(:)%prnt )                     !--- Parent index
803  ngen = MAXVAL(tr(:)%igen, MASK=.TRUE.)
804  DO iq = 1, SIZE(tr)
805    ng = tr(iq)%igen                                                 !--- Generation of the current tracer
806    ix = idxAncestor(tr, igen = ng); ix = PACK(ix, ix/=0)            !--- Indexes of the tracers with ancestor tr(iq)
807    !--- Childs indexes in growing generation order
808    tr(iq)%idesc = [( PACK(ix, MASK = tr(ix)%igen == ig), ig = ng+1, ngen)]
809    tr(iq)%ndesc =     SUM(  [( COUNT(tr(ix)%igen == ig), ig = ng+1, ngen)] )
810    tr(iq)%nchld =              COUNT(tr(ix)%igen == ng+1)
811  END DO
812END SUBROUTINE indexUpdate
813!------------------------------------------------------------------------------------------------------------------------------
814 
815 
816!==============================================================================================================================
817!=== READ FILE "fnam" TO APPEND THE "dBase" TRACERS DATABASE WITH AS MUCH SECTIONS AS PARENTS NAMES IN "isot(:)%prnt":     ====
818!===  * Each section dBase(i)%name contains the isotopes "dBase(i)%trac(:)" descending on "dBase(i)%name"="iso(i)%prnt"    ====
819!===  * For each isotopes class, the <key>=<val> vector of each tracer is moved into the isotopes descriptor "isot"        ====
820!=== NOTES:                                                                                                                ====
821!===  * Most of the "isot" components have been defined in the calling routine (initIsotopes):                             ====
822!===      prnt,   nzon, zone(:),   niso, keys(:)%name,   nitr, trac(:),   npha, phas,  iTraPha(:,:),  iZonPhi(:,:)         ====
823!===  * Same syntax for isotopes file and "tracer.def": a tracers section contains one line for each of its isotopes       ====
824!===  * Each tracers section can contain a "params" virtual isotope line of isotopes parameters default values             ====
825!===  * In case keys are found both in the "params" section and the "*.def" file, the later value is retained              ====
826!===  * On each isotope line, defined keys can be used for other keys defintions (single level depth substitution)         ====
827!===  * The routine gives an error if a required isotope is not available in the database stored in "fnam"                 ====
828!==============================================================================================================================
829LOGICAL FUNCTION readIsotopesFile(fnam, isot) RESULT(lerr)
830  CHARACTER(LEN=*),  INTENT(IN)    :: fnam                           !--- Input file name
831  TYPE(iso), TARGET, INTENT(INOUT) :: isot(:)                        !--- Isotopes descriptors (field "prnt" must be defined !)
832  INTEGER :: ik, is, it, idb, nk0, i, iis
833  INTEGER :: nk, ns, nt, ndb, nb0, i0
834  CHARACTER(LEN=256), POINTER     :: k(:), v(:), k0(:), v0(:)
835  CHARACTER(LEN=256), ALLOCATABLE :: vals(:)
836  CHARACTER(LEN=256)     :: val
837  TYPE(kys),    POINTER  ::   ky(:)
838  TYPE(tra),    POINTER  ::   tt(:), t
839  TYPE(db),  ALLOCATABLE ::  tdb(:)
840  LOGICAL,   ALLOCATABLE :: liso(:)
841
842  !--- THE INPUT FILE MUST BE PRESENT
843  IF(test(fmsg(testFile(fnam),'Missing isotopes parameters file "'//TRIM(fnam)//'"'),lerr)) RETURN
844
845  !--- READ THE FILE SECTIONS, ONE EACH PARENT TRACER
846  nb0 = SIZE(dBase, DIM=1)+1                                         !--- Next database element index
847  IF(test(readSections(fnam,strStack(isot(:)%prnt,',')),lerr)) RETURN!--- Read sections, one each parent tracer
848  ndb = SIZE(dBase, DIM=1)                                           !--- Current database size
849  DO idb = nb0, ndb
850   iis = idb-nb0+1
851
852    !--- GET FEW GLOBAL KEYS FROM "def" FILES AND ADD THEM TO THE 'params' SECTION
853    CALL addKeysFromDef(dBase(idb)%trac, 'params')
854
855    !--- SUBSTITUTE THE KEYS DEFINED IN THE 'params' VIRTUAL TRACER ; SUBSTITUTE LOCAL KEYS ; REMOVE 'params' VIRTUAL TRACER
856    CALL subDefault(dBase(idb)%trac, 'params', .TRUE.)
857
858    tt => dBase(idb)%trac
859
860    !--- REDUCE THE EXPRESSIONS TO OBTAIN SCALARS AND TRANSFER THEM TO THE "isot" ISOTOPES DESCRIPTORS VECTOR
861    DO it = 1, SIZE(dBase(idb)%trac)
862      t => dBase(idb)%trac(it)
863      is = strIdx(isot(iis)%keys(:)%name, t%name)                    !--- Index in "isot(iis)%keys(:)%name" of isotope "t%name"
864      IF(is == 0) CYCLE
865      liso = reduceExpr(t%keys%val, vals)                            !--- Reduce expressions (for substituted variables)
866      IF(test(ANY(liso), lerr)) RETURN                               !--- Some non-numerical elements were found
867      isot(iis)%keys(is)%key = PACK(t%keys%key, MASK=.NOT.liso)
868      isot(iis)%keys(is)%val = PACK(  vals,     MASK=.NOT.liso)
869    END DO
870
871    !--- CHECK FOR MISSING ISOTOPES (NO KEYS ALLOCATED)
872    liso = [( ALLOCATED(isot(iis)%keys(is)%key), is=1, SIZE(isot(iis)%keys) )]
873    IF(test(checkList(isot(iis)%keys(:)%name, .NOT.liso, &
874      'Check file "'//TRIM(fnam)//'" in section "'//TRIM(dBase(idb)%name)//'"', 'isotopes', 'missing'),lerr)) RETURN
875  END DO
876
877  !--- CLEAN THE DATABASE ENTRIES
878  IF(nb0 == 1) THEN
879    DEALLOCATE(dBase); ALLOCATE(dBase(0))
880  ELSE
881    ALLOCATE(tdb(nb0-1)); tdb(1:nb0-1)=dBase(1:nb0-1); CALL MOVE_ALLOC(FROM=tdb, TO=dBase)
882  END IF
883  lerr = dispIsotopes(isot, 'Isotopes parameters read from file')
884
885END FUNCTION readIsotopesFile
886!==============================================================================================================================
887
888!==============================================================================================================================
889!=== IF ISOTOPES (2ND GENERATION TRACERS) ARE DETECTED:                                                                     ===
890!===    * COMPUTE MOST OF THE RELATED QUANTITIES ("isot" COMPONENTS).                                                       ===
891!===    * COMPUTE FEW ISOTOPES-DEDICATED "trac" COMPONENTS                                                                  ===
892!===    * CALL readIsotopesFile TO GET PHYSICAL QUANTITIES (<key>=<val> PAIRS)                                              ===
893!===      NOTE: THIS IS DONE HERE (IN A ROUTINE CALLED BY THE DYNAMIC), BECAUSE THE DYNAMIC NEEDS FEW PHYSICAL PARAMETERS.  ===
894!==============================================================================================================================
895SUBROUTINE initIsotopes(trac, isot)
896  TYPE(tra), ALLOCATABLE, TARGET, INTENT(INOUT) :: trac(:)
897  TYPE(iso), ALLOCATABLE, TARGET, INTENT(INOUT) :: isot(:)
898  CHARACTER(LEN=256), ALLOCATABLE :: p(:), str(:)                    !--- Temporary storage
899  CHARACTER(LEN=256) :: iname
900  CHARACTER(LEN=1)   :: ph                                           !--- Phase
901  INTEGER :: nbIso, ic, ip, iq, it, iz
902  LOGICAL, ALLOCATABLE :: ll(:)                                      !--- Mask
903  TYPE(tra), POINTER   ::  t(:), t1
904  TYPE(iso), POINTER   ::  s
905
906  t => trac
907
908  p = PACK(delPhase(t%prnt), MASK = t%type=='tracer' .AND. t%igen==2)!--- Parents of 2nd generation isotopes
909  CALL strReduce(p, nbIso)
910  ALLOCATE(isot(nbIso))
911
912  IF(nbIso==0) RETURN                                                !=== NO ISOTOPES: FINISHED
913
914  !--- ISOTOPES RELATED VARIABLES ; NULL OR EMPTY IF NO ISOTOPES
915  isot(:)%prnt = p
916  DO ic = 1, SIZE(p)                                                 !--- Loop on isotopes classes
917    s => isot(ic)
918    iname = s%prnt                                                   !--- Current isotopes class name (parent tracer name)
919
920    !=== Isotopes childs of tracer "iname": mask, names, number (same for each phase of "iname")
921    ll = t(:)%type=='tracer' .AND. delPhase(t(:)%prnt) == iname .AND. t(:)%phas == 'g'
922    str = PACK(delPhase(t(:)%name), MASK = ll)                       !--- Effectively found isotopes of "iname"
923    s%niso = SIZE(str)                                               !--- Number of "effectively found isotopes of "iname"
924    ALLOCATE(s%keys(s%niso))
925    FORALL(it = 1:s%niso) s%keys(it)%name = str(it)
926
927    !=== Geographic tagging tracers descending on tracer "iname": mask, names, number
928    ll = t(:)%type=='tag'    .AND. delPhase(t(:)%nam1) == iname .AND. t(:)%igen == 3
929    s%zone = PACK(strTail(t(:)%name,'_',lFirst=.TRUE.), MASK = ll)   !--- Tagging zones names  for isotopes category "iname"
930    CALL strReduce(s%zone)
931    s%nzon = SIZE(s%zone)                                            !--- Tagging zones number for isotopes category "iname"
932
933    !=== Geographic tracers of the isotopes childs of tracer "iname" (same for each phase of "iname")
934    !    NOTE: One might like to create a similar variable for 2nd generation tagging tracers (tagging the gen1 tracers)
935    str = PACK(delPhase(t(:)%name), MASK=ll)
936    CALL strReduce(str)
937    s%nitr = s%niso + SIZE(str)                                      !--- Number of isotopes + their geographic tracers [ntraciso]
938    ALLOCATE(s%trac(s%nitr))
939    FORALL(it = 1:s%niso) s%trac(it) = s%keys(it)%name
940    FORALL(it = s%niso+1:s%nitr) s%trac(it) = str(it-s%niso)
941
942    !=== Phases for tracer "iname"
943    s%phas = ''
944    DO ip = 1, nphases; ph = known_phases(ip:ip); IF(strIdx(t%name,addPhase(iname, ph)) /= 0) s%phas = TRIM(s%phas)//ph; END DO
945    s%npha = LEN_TRIM(s%phas)                                        !--- Equal to "nqo" for water
946
947    !=== Tables giving the index in a table of effectively found items for each dynamical tracer (1<=iq<=nqtot)
948    DO iq = 1, SIZE(t)
949      t1 => trac(iq)
950      IF(delPhase(t1%nam1) /= iname) CYCLE                            !--- Only deal with tracers descending on "iname"
951      t1%iso_igr = ic                                                 !--- Isotopes family       idx in list "isotopes(:)%prnt"
952      t1%iso_num = strIdx(s%trac, delPhase(strHead(t1%name,'_')))     !--- Current isotope       idx in effective isotopes list
953      t1%iso_zon = strIdx(s%zone,          strTail(t1%name,'_') )     !--- Current isotope zone  idx in effective zones    list
954      t1%iso_pha =  INDEX(s%phas,TRIM(t1%phas))                       !--- Current isotope phase idx in effective phases   list
955      IF(t1%igen /= 3) t1%iso_zon = 0                                 !--- Skip possible generation 2 tagging tracers
956    END DO
957
958    !=== Table used to get iq (index in dyn array, size nqtot) from the isotope and phase indexes ; the full isotopes list
959    !    (including tagging tracers) is sorted this way:  iso1, iso2, ..., iso1_zone1, iso2_zone1, ..., iso1_zoneN, iso2_zoneN
960    s%iTraPha = RESHAPE( [( (strIdx(t(:)%name,  addPhase(s%trac(it),s%phas(ip:ip))),     it=1, s%nitr), ip=1, s%npha)], &
961                         [s%nitr, s%npha] )
962
963    !=== Table used to get ix (index in tagging tracers isotopes list, size nitr) from the zone and isotope indexes
964    s%iZonIso = RESHAPE( [( (strIdx(s%trac(:), TRIM(s%trac(it))//'_'//TRIM(s%zone(iz))), iz=1, s%nzon), it=1, s%niso)], &
965                         [s%nzon, s%niso] )
966  END DO
967 
968  !=== Indexes, in dynamical tracers list, of the tracers transmitted to phytrac (nqtottr non-vanishing elements)
969  ll = delPhase(t%name)/='H2O' .AND. t%iso_num ==0              !--- Mask of tracers passed to the physics
970  t(:)%itr = UNPACK([(iq,iq=1,COUNT(ll))], ll, [(0, iq=1, SIZE(t))])
971
972  !=== READ PHYSICAL PARAMETERS FROM "isotopes_params.def" FILE
973  !    DONE HERE, AND NOT ONLY IN "infotrac_phy", BECAUSE SOME PHYSICAL PARAMS ARE NEEDED FOR RESTARTS (tnat AND alpha_ideal)
974  IF(readIsotopesFile('isotopes_params.def',isot)) CALL abort_gcm(modname,'Problem when reading isotopes parameters',1)
975
976END SUBROUTINE initIsotopes
977!==============================================================================================================================
978
979
980!==============================================================================================================================
981LOGICAL FUNCTION dispIsotopes(ides, message) RESULT(lerr)
982  TYPE(iso),        INTENT(IN) :: ides(:)                            !--- Isotopes descriptor vector
983  CHARACTER(LEN=*), INTENT(IN) :: message                            !--- Message to display
984  INTEGER :: ik, nk, ip, it, nt
985  CHARACTER(LEN=256) :: prf
986  CHARACTER(LEN=256), ALLOCATABLE :: ttl(:), val(:,:)
987  CALL msg(TRIM(message)//':')
988  DO ip = 1, SIZE(ides)                                              !--- Loop on parents tracers
989    nk = SIZE(ides(ip)%keys(1)%key)                                  !--- Same keys for each isotope
990    nt = SIZE(ides(ip)%keys)                                         !--- Number of isotopes
991    prf = 'i'//REPEAT('s',nk+1)                                      !--- Profile for table printing
992    ALLOCATE(ttl(nk+2), val(nt,nk+1))
993    ttl(1:2) = ['iq  ','name']; ttl(3:nk+2) = ides(ip)%keys(1)%key(:)!--- Titles line with keys names
994    val(:,1) = ides(ip)%keys(:)%name                                 !--- Values table 1st column: isotopes names 
995    DO ik = 1, nk
996      DO it = 1, nt
997        val(it,ik+1) = ides(ip)%keys(it)%val(ik)                     !--- Other columns: keys values
998      END DO
999    END DO
1000    IF(test(fmsg(dispTable(prf, ttl, val, cat([(it,it=1,nt)]), rFmt='(EN8.4)'),'Problem with the table content'), lerr)) RETURN
1001    DEALLOCATE(ttl, val)
1002  END DO       
1003END FUNCTION dispIsotopes
1004!==============================================================================================================================
1005
1006
1007!==============================================================================================================================
1008SUBROUTINE addKey_1(key, val, ky, lOverWrite)
1009!------------------------------------------------------------------------------------------------------------------------------
1010! Purpose: Add the <key>=<val> pair in the "ky" keys descriptor.
1011!------------------------------------------------------------------------------------------------------------------------------
1012  CHARACTER(LEN=*),  INTENT(IN)    :: key, val
1013  TYPE(kys),         INTENT(INOUT) :: ky
1014  LOGICAL, OPTIONAL, INTENT(IN)    :: lOverWrite
1015  CHARACTER(LEN=256),  ALLOCATABLE :: k(:), v(:)
1016  INTEGER :: iky, nky
1017  LOGICAL :: lo
1018!------------------------------------------------------------------------------------------------------------------------------
1019  lo=.FALSE.; IF(PRESENT(lOverWrite)) lo=lOverWrite
1020  iky = strIdx(ky%key,key)
1021  IF(iky == 0) THEN
1022    nky = SIZE(ky%key)
1023    IF(nky == 0) THEN; ky%key = [key]; ky%val = [val]; ELSE; ky%key = [ky%key, key]; ky%val = [ky%val, val]; END IF
1024  ELSE IF(lo) THEN                                                   !--- Overwriting
1025    ky%key(iky) = key; ky%val(iky) = val
1026  END IF
1027END SUBROUTINE addKey_1
1028!==============================================================================================================================
1029SUBROUTINE addKey_m(key, val, ky, lOverWrite)
1030!------------------------------------------------------------------------------------------------------------------------------
1031! Purpose: Add the <key>=<val> pair in all the components of the "ky" keys descriptor.
1032!------------------------------------------------------------------------------------------------------------------------------
1033  CHARACTER(LEN=*),  INTENT(IN)    :: key, val
1034  TYPE(kys),         INTENT(INOUT) :: ky(:)
1035  LOGICAL, OPTIONAL, INTENT(IN)    :: lOverWrite
1036  INTEGER :: itr
1037  LOGICAL :: lo
1038!------------------------------------------------------------------------------------------------------------------------------
1039  lo=.FALSE.; IF(PRESENT(lOverWrite)) lo=lOverWrite
1040  DO itr = 1, SIZE(ky); CALL addKey_1(key, val, ky(itr), lo); END DO
1041END SUBROUTINE addKey_m
1042!==============================================================================================================================
1043SUBROUTINE addKeysFromDef(t, tr0)
1044!------------------------------------------------------------------------------------------------------------------------------
1045! Purpose: The values of the keys of the tracer named "tr0" are overwritten by the values found in the *.def files, if any.
1046!------------------------------------------------------------------------------------------------------------------------------
1047  TYPE(tra), ALLOCATABLE, INTENT(INOUT) :: t(:)
1048  CHARACTER(LEN=*),       INTENT(IN)    :: tr0
1049  CHARACTER(LEN=256) :: val
1050  INTEGER            :: ik, jd
1051  jd = strIdx(t%name, tr0)
1052  IF(jd == 0) RETURN
1053  DO ik = 1, SIZE(t(jd)%keys%key)
1054    CALL get_in(t(jd)%keys%key(ik), val, 'zzzz')
1055    IF(val /= 'zzzz') CALL addKey_1(t(jd)%keys%key(ik), val, t(jd)%keys, .TRUE.)
1056  END DO
1057END SUBROUTINE addKeysFromDef
1058!==============================================================================================================================
1059SUBROUTINE delKey_1(itr, keyn, ky)
1060!------------------------------------------------------------------------------------------------------------------------------
1061! Purpose: Internal routine.
1062!   Remove <key>=<val> pairs in the "itr"th component of the "ky" keys descriptor.
1063!------------------------------------------------------------------------------------------------------------------------------
1064  INTEGER,          INTENT(IN)    :: itr
1065  CHARACTER(LEN=*), INTENT(IN)    :: keyn(:)
1066  TYPE(tra),        INTENT(INOUT) :: ky(:)
1067  CHARACTER(LEN=256), ALLOCATABLE :: k(:), v(:)
1068  LOGICAL,  ALLOCATABLE :: ll(:)
1069  INTEGER :: iky
1070!------------------------------------------------------------------------------------------------------------------------------
1071  IF(itr<=0 .OR. itr>SIZE(ky, DIM=1)) RETURN                          !--- Index is out of range
1072  ll = [( ALL(keyn/=ky(itr)%keys%key(iky)), iky=1, SIZE(ky(itr)%keys%key) )]
1073  k = PACK(ky(itr)%keys%key, MASK=ll); CALL MOVE_ALLOC(FROM=k, TO=ky(itr)%keys%key)
1074  v = PACK(ky(itr)%keys%val, MASK=ll); CALL MOVE_ALLOC(FROM=v, TO=ky(itr)%keys%val)
1075END SUBROUTINE delKey_1
1076!==============================================================================================================================
1077SUBROUTINE delKey(keyn, ky)
1078!------------------------------------------------------------------------------------------------------------------------------
1079! Purpose: Internal routine.
1080!   Remove <key>=<val> pairs in all the components of the "t" tracers descriptor.
1081!------------------------------------------------------------------------------------------------------------------------------
1082  CHARACTER(LEN=*), INTENT(IN)    :: keyn(:)
1083  TYPE(tra),        INTENT(INOUT) :: ky(:)
1084  INTEGER :: iky
1085!------------------------------------------------------------------------------------------------------------------------------
1086  DO iky = 1, SIZE(ky); CALL delKey_1(iky, keyn, ky); END DO
1087END SUBROUTINE delKey
1088!==============================================================================================================================
1089
1090
1091!==============================================================================================================================
1092!=== PUBLIC ROUTINES: GET A KEY FROM A <key>=<val> LIST ; VECTORS, TRACER AND DATABASE VERSIONS ===============================
1093!=== BEWARE !!! IF THE "ky" ARGUMENT IS NOT PRESENT, THEN THE VARIABLES "tracers" AND "isotopes" ARE USED. ====================
1094!===     THEY ARE LOCAL TO THIS MODULE, SO MUST MUST BE INITIALIZED FIRST USING the "getKey_init" ROUTINE  ====================
1095!==============================================================================================================================
1096SUBROUTINE getKey_init(tracers_, isotopes_)
1097  TYPE(tra), OPTIONAL, INTENT(IN) ::  tracers_(:)
1098  TYPE(iso), OPTIONAL, INTENT(IN) :: isotopes_(:)
1099  IF(PRESENT( tracers_))  tracers =  tracers_
1100  IF(PRESENT(isotopes_)) isotopes = isotopes_
1101END SUBROUTINE getKey_init
1102!==============================================================================================================================
1103CHARACTER(LEN=256) FUNCTION fgetKey(itr, keyn, ky, def_val) RESULT(out)
1104!------------------------------------------------------------------------------------------------------------------------------
1105! Purpose: Internal function ; get a key value in string format (this is the returned argument).
1106!------------------------------------------------------------------------------------------------------------------------------
1107  INTEGER,                    INTENT(IN) :: itr
1108  CHARACTER(LEN=*),           INTENT(IN) :: keyn
1109  TYPE(kys),                  INTENT(IN) :: ky(:)
1110  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: def_val
1111!------------------------------------------------------------------------------------------------------------------------------
1112  INTEGER :: ik
1113  ik = 0; IF(itr>0 .AND. itr<=SIZE(ky)) ik = strIdx(ky(itr)%key(:), keyn)
1114  out = '';              IF(ik /= 0) out = ky(itr)%val(ik)           !--- Key was found
1115  IF(PRESENT(def_val) .AND. ik == 0) out = def_val                   !--- Default value from arguments
1116END FUNCTION fgetKey
1117!==============================================================================================================================
1118LOGICAL FUNCTION getKeyByName_s1(keyn, val, tname, ky) RESULT(lerr)
1119  !--- Purpose: Get the value of the key named "keyn" for the tracer named "tnam".
1120  !     * "ky" unspecified: try in "tracers" for "tnam" with phase and tagging suffixes, then in "isotopes" without.
1121  !     * "ky"   specified: try in "ky"      for "tnam" with phase and tagging suffixes, then without.
1122  !    The returned error code is always .FALSE.: an empty string is returned when the key hasn't been found.
1123  CHARACTER(LEN=*),    INTENT(IN)  :: keyn
1124  CHARACTER(LEN=256),  INTENT(OUT) :: val
1125  CHARACTER(LEN=*),    INTENT(IN)  :: tname
1126  TYPE(kys), OPTIONAL, INTENT(IN)  :: ky(:)
1127  INTEGER :: is
1128  lerr = .FALSE.
1129  IF(PRESENT(ky)) THEN
1130    val = getKeyByName_prv(keyn, tname , ky);    IF(val /= '') RETURN          !--- "ky" and "tnam"
1131    val = getKeyByName_prv(keyn, delPhase(strHead(tname,'_')), ky)             !--- "ky" and "tnam" without phase
1132  ELSE
1133    IF(.NOT.ALLOCATED(tracers))  RETURN
1134    val = getKeyByName_prv(keyn, tname, tracers(:)%keys); IF(val /= '') RETURN !--- "tracers" and "tnam"
1135    IF(.NOT.ALLOCATED(isotopes)) RETURN
1136    IF(SIZE(isotopes) == 0)      RETURN
1137    DO is = 1, SIZE(isotopes); IF(strIdx(isotopes(is)%keys(:)%name, delPhase(strHead(tname,'_'))) /= 0) EXIT; END DO
1138    IF(is /= 0) val = getKeyByName_prv(keyn, tname, isotopes(is)%keys(:))      !--- "isotopes" and "tnam" without phase
1139  END IF
1140
1141CONTAINS
1142
1143FUNCTION getKeyByName_prv(keyn, tname, ky) RESULT(val)
1144  CHARACTER(LEN=256)            :: val
1145  CHARACTER(LEN=*), INTENT(IN)  :: keyn
1146  CHARACTER(LEN=*), INTENT(IN)  :: tname
1147  TYPE(kys),        INTENT(IN)  :: ky(:)
1148  INTEGER :: itr, iky
1149  val = ''; iky = 0
1150  itr = strIdx(ky(:)%name, tname);                 IF(itr==0) RETURN           !--- Get the index of the wanted tracer
1151  IF(itr /= 0) iky = strIdx(ky(itr)%key(:), keyn); IF(iky==0) RETURN           !--- Wanted key    index
1152  val = ky(itr)%val(iky)
1153END FUNCTION getKeyByName_prv
1154
1155END FUNCTION getKeyByName_s1
1156!==============================================================================================================================
1157LOGICAL FUNCTION getKeyByName_sm(keyn, val, tnam, ky) RESULT(lerr)
1158  CHARACTER(LEN=*),                   INTENT(IN)  :: keyn
1159  CHARACTER(LEN=256),    ALLOCATABLE, INTENT(OUT) ::  val(:)
1160  CHARACTER(LEN=*), TARGET, OPTIONAL, INTENT(IN)  :: tnam(:)
1161  TYPE(kys),        TARGET, OPTIONAL, INTENT(IN)  ::   ky(:)
1162  CHARACTER(LEN=256), POINTER :: n(:)
1163  INTEGER :: iq
1164  n => tracers(:)%keys%name; IF(PRESENT(tnam)) n => tnam(:)
1165  ALLOCATE(val(SIZE(n)))
1166  IF(     PRESENT(ky)) lerr = ANY([(getKeyByName_s1(keyn, val(iq), n(iq), ky), iq=1, SIZE(n))])
1167  IF(.NOT.PRESENT(ky)) lerr = ANY([(getKeyByName_s1(keyn, val(iq), n(iq)),     iq=1, SIZE(n))])
1168END FUNCTION getKeyByName_sm
1169!==============================================================================================================================
1170LOGICAL FUNCTION getKeyByName_i1(keyn, val, tnam, ky) RESULT(lerr)
1171  CHARACTER(LEN=*),    INTENT(IN)  :: keyn
1172  INTEGER,             INTENT(OUT) :: val
1173  CHARACTER(LEN=*),    INTENT(IN)  :: tnam
1174  TYPE(kys), OPTIONAL, INTENT(IN)  :: ky(:)
1175  CHARACTER(LEN=256) :: sval
1176  INTEGER :: ierr
1177  IF(     PRESENT(ky)) lerr = getKeyByName_s1(keyn, sval, tnam, ky)
1178  IF(.NOT.PRESENT(ky)) lerr = getKeyByName_s1(keyn, sval, tnam)
1179  IF(test(fmsg(lerr,   'key "'//TRIM(keyn)//'" or tracer "'//TRIM(tnam)//'" is missing'),        lerr)) RETURN
1180  READ(sval, *, IOSTAT=ierr) val
1181  IF(test(fmsg(ierr/=0,'key "'//TRIM(keyn)//'" of tracer "'//TRIM(tnam)//'" is not an integer'), lerr)) RETURN
1182END FUNCTION getKeyByName_i1
1183!==============================================================================================================================
1184LOGICAL FUNCTION getKeyByName_im(keyn, val, tnam, ky) RESULT(lerr)
1185  CHARACTER(LEN=*),                   INTENT(IN)  :: keyn
1186  INTEGER,               ALLOCATABLE, INTENT(OUT) ::  val(:)
1187  CHARACTER(LEN=*), TARGET, OPTIONAL, INTENT(IN)  :: tnam(:)
1188  TYPE(kys),        TARGET, OPTIONAL, INTENT(IN)  ::   ky(:)
1189  CHARACTER(LEN=256), POINTER :: n(:)
1190  INTEGER :: iq
1191  n => tracers(:)%name; IF(PRESENT(tnam)) n => tnam(:)
1192  ALLOCATE(val(SIZE(n)))
1193  IF(     PRESENT(ky)) lerr = ANY([(getKeyByName_i1(keyn, val(iq), n(iq), ky), iq=1, SIZE(n))])
1194  IF(.NOT.PRESENT(ky)) lerr = ANY([(getKeyByName_i1(keyn, val(iq), n(iq)),     iq=1, SIZE(n))])
1195END FUNCTION getKeyByName_im
1196!==============================================================================================================================
1197LOGICAL FUNCTION getKeyByName_r1(keyn, val, tnam, ky) RESULT(lerr)
1198  CHARACTER(LEN=*),    INTENT(IN)  :: keyn
1199  REAL,                INTENT(OUT) :: val
1200  CHARACTER(LEN=*),    INTENT(IN)  :: tnam
1201  TYPE(kys), OPTIONAL, INTENT(IN)  :: ky(:)
1202  CHARACTER(LEN=256) :: sval
1203  INTEGER :: ierr
1204  IF(     PRESENT(ky)) lerr = getKeyByName_s1(keyn, sval, tnam, ky)
1205  IF(.NOT.PRESENT(ky)) lerr = getKeyByName_s1(keyn, sval, tnam)
1206  IF(test(fmsg(lerr,   'key "'//TRIM(keyn)//'" or tracer "'//TRIM(tnam)//'" is missing'),    lerr)) RETURN
1207  READ(sval, *, IOSTAT=ierr) val
1208  IF(test(fmsg(ierr/=0,'key "'//TRIM(keyn)//'" of tracer "'//TRIM(tnam)//'" is not a real'), lerr)) RETURN
1209END FUNCTION getKeyByName_r1
1210!==============================================================================================================================
1211LOGICAL FUNCTION getKeyByName_rm(keyn, val, tnam, ky) RESULT(lerr)
1212  CHARACTER(LEN=*),                   INTENT(IN)  :: keyn
1213  REAL,                  ALLOCATABLE, INTENT(OUT) ::  val(:)
1214  CHARACTER(LEN=*), TARGET, OPTIONAL, INTENT(IN)  :: tnam(:)
1215  TYPE(kys),        TARGET, OPTIONAL, INTENT(IN)  ::   ky(:)
1216  CHARACTER(LEN=256), POINTER :: n(:)
1217  INTEGER :: iq
1218  n => tracers(:)%name;  IF(PRESENT(tnam)) n => tnam(:)
1219  ALLOCATE(val(SIZE(n)))
1220  IF(     PRESENT(ky)) lerr = ANY([(getKeyByName_r1(keyn, val(iq), n(iq), ky), iq=1, SIZE(n))])
1221  IF(.NOT.PRESENT(ky)) lerr = ANY([(getKeyByName_r1(keyn, val(iq), n(iq)),     iq=1, SIZE(n))])
1222END FUNCTION getKeyByName_rm
1223!==============================================================================================================================
1224
1225
1226!==============================================================================================================================
1227!=== REMOVE, IF ANY, OR ADD THE PHASE SUFFIX OF THE TRACER NAMED "s" ==========================================================
1228!==============================================================================================================================
1229ELEMENTAL CHARACTER(LEN=256) FUNCTION delPhase(s) RESULT(out)
1230  CHARACTER(LEN=*), INTENT(IN) :: s
1231  INTEGER :: l, i, ix
1232  out = s
1233  IF(s == '') RETURN                                                           !--- Empty string: nothing to do
1234  !--- Index of found phase in "known_phases"
1235  ix = MAXLOC( [(i, i=1,nphases)], MASK=[( INDEX(s, phases_sep//known_phases(i:i))/=0, i=1, nphases)], DIM=1 )
1236  IF(ix == 0) RETURN                                                           !--- No phase pattern found
1237  i = INDEX(s, phases_sep//known_phases(ix:ix))                                !--- Index of <sep><pha> pattern in "str"
1238  l = LEN_TRIM(s)
1239  IF(i == l-1) THEN                                                            !--- <var><sep><pha>       => return <var>
1240    out = s(1:l-2)
1241  ELSE IF(s(i+2:i+2) == '_') THEN                                              !--- <var><sep><pha>_<tag> => return <var>_<tag>
1242    out = s(1:i-1)//s(i+2:l)
1243  END IF
1244END FUNCTION delPhase
1245!------------------------------------------------------------------------------------------------------------------------------
1246CHARACTER(LEN=256) FUNCTION addPhase_1(s,pha) RESULT(out)
1247  CHARACTER(LEN=*), INTENT(IN) :: s
1248  CHARACTER(LEN=1), INTENT(IN) :: pha
1249  INTEGER :: l, i
1250  out = s
1251  IF(s == '') RETURN                                                           !--- Empty string: nothing to do
1252  i = INDEX(s, '_')                                                            !--- /=0 for <var>_<tag> tracers names
1253  l = LEN_TRIM(s)
1254  IF(i == 0) out =  TRIM(s)//phases_sep//pha                                   !--- <var>       => return <var><sep><pha>
1255  IF(i /= 0) out = s(1:i-1)//phases_sep//pha//'_'//s(i+1:l)                    !--- <var>_<tag> => return <var><sep><pha>_<tag>
1256END FUNCTION addPhase_1
1257!------------------------------------------------------------------------------------------------------------------------------
1258FUNCTION addPhase_m(s,pha) RESULT(out)
1259  CHARACTER(LEN=*),    INTENT(IN) :: s(:)
1260  CHARACTER(LEN=1),    INTENT(IN) :: pha
1261  CHARACTER(LEN=256), ALLOCATABLE :: out(:) 
1262  INTEGER :: k
1263  out = [( addPhase_1(s(k), pha), k=1, SIZE(s) )]
1264END FUNCTION addPhase_m
1265!------------------------------------------------------------------------------------------------------------------------------
1266
1267
1268!==============================================================================================================================
1269!=== GET THE NAME(S) OF THE ANCESTOR(S) OF TRACER(S) "tname" AT GENERATION "igen"  IN THE TRACERS DESCRIPTORS LIST "tr" =======
1270!==============================================================================================================================
1271CHARACTER(LEN=256) FUNCTION ancestor_1(t, tname, igen) RESULT(out)
1272  TYPE(tra),         INTENT(IN) :: t(:)
1273  CHARACTER(LEN=*),  INTENT(IN) :: tname
1274  INTEGER, OPTIONAL, INTENT(IN) :: igen
1275  INTEGER :: ig, ix
1276  ig = 1; IF(PRESENT(igen)) ig = igen
1277  ix = idxAncestor_1(t, tname, ig)
1278  out = ''; IF(ix /= 0) out = t(ix)%name
1279END FUNCTION ancestor_1
1280!------------------------------------------------------------------------------------------------------------------------------
1281FUNCTION ancestor_m(t, tname, igen) RESULT(out)
1282  CHARACTER(LEN=256), ALLOCATABLE        ::   out(:)
1283  TYPE(tra),                  INTENT(IN) ::     t(:)
1284  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: tname(:)
1285  INTEGER,          OPTIONAL, INTENT(IN) :: igen
1286  INTEGER, ALLOCATABLE :: ix(:)
1287  INTEGER :: ig
1288  ig = 1; IF(PRESENT(igen)) ig = igen
1289  IF(     PRESENT(tname)) ix = idxAncestor_m(t, tname,     ig)
1290  IF(.NOT.PRESENT(tname)) ix = idxAncestor_m(t, t(:)%name, ig)
1291  ALLOCATE(out(SIZE(ix))); out(:) = ''
1292  WHERE(ix /= 0) out = t(ix)%name
1293END FUNCTION ancestor_m
1294!==============================================================================================================================
1295
1296
1297!==============================================================================================================================
1298!=== GET THE INDEX(ES) OF THE ANCESTOR(S) OF TRACER(S) "tname" AT GENERATION "igen"  IN THE TRACERS DESCRIPTORS LIST "tr" =====
1299!==============================================================================================================================
1300INTEGER FUNCTION idxAncestor_1(t, tname, igen) RESULT(out)
1301! Return the name of the generation "igen" ancestor of "tname"
1302  TYPE(tra),         INTENT(IN) :: t(:)
1303  CHARACTER(LEN=*),  INTENT(IN) :: tname
1304  INTEGER, OPTIONAL, INTENT(IN) :: igen
1305  INTEGER :: ig
1306  ig = 1; IF(PRESENT(igen)) ig = igen
1307  out = strIdx(t(:)%name, tname)
1308  IF(out == 0)          RETURN
1309  IF(t(out)%igen <= ig) RETURN
1310  DO WHILE(t(out)%igen > ig); out = strIdx(t(:)%name, t(out)%prnt); END DO
1311END FUNCTION idxAncestor_1
1312!------------------------------------------------------------------------------------------------------------------------------
1313FUNCTION idxAncestor_m(t, tname, igen) RESULT(out)
1314  INTEGER,          ALLOCATABLE          ::   out(:)
1315  TYPE(tra),                  INTENT(IN) ::     t(:)
1316  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: tname(:)
1317  INTEGER,          OPTIONAL, INTENT(IN) :: igen
1318  INTEGER :: ig, ix
1319  ig = 1; IF(PRESENT(igen)) ig = igen
1320  IF(     PRESENT(tname)) out = [(idxAncestor_1(t, tname(ix),  ig), ix=1, SIZE(tname))]
1321  IF(.NOT.PRESENT(tname)) out = [(idxAncestor_1(t, t(ix)%name, ig), ix=1, SIZE(t))]
1322END FUNCTION idxAncestor_m
1323!==============================================================================================================================
1324
1325
1326END MODULE readTracFiles_mod
Note: See TracBrowser for help on using the repository browser.