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

Last change on this file since 3961 was 3957, checked in by dcugnet, 3 years ago

In readTracFiles: the separator between the tracer name and its phase is no longer hardcoded and equal to "-",
but is a parameter ("phases_sep") which default value is "_".
Few more fixes.

File size: 84.0 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(test(fmsg(fType==0, 'No adequate tracers description file(s) found ; default values will be used'), lerr)) 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      is = strIdx(isot(iis)%keys(:)%name, dBase(idb)%trac(it)%name)  !--- Index of the "isot(iis)%keys(:)%name" tracer named "t%name"
863      IF(is == 0) CYCLE
864      t => dBase(idb)%trac(it)
865      liso = reduceExpr(t%keys%val, vals)                            !--- Reduce expressions (for substituted variables)
866      isot(iis)%keys(is)%key = PACK(t%keys%key, MASK=liso)
867      isot(iis)%keys(is)%val = PACK(  vals,     MASK=liso)
868    END DO
869
870    !--- CHECK FOR MISSING ISOTOPES (NO KEYS ALLOCATED)
871    liso = [( ALLOCATED(isot(iis)%keys(is)%key), is=1, SIZE(isot(iis)%keys) )]
872    IF(test(checkList(isot(iis)%keys(:)%name, .NOT.liso, &
873      'Check file "'//TRIM(fnam)//'" in section "'//TRIM(dBase(idb)%name)//'"', 'isotopes', 'missing'),lerr)) RETURN
874  END DO
875
876  !--- CLEAN THE DATABASE ENTRIES
877  IF(nb0 == 1) THEN
878    DEALLOCATE(dBase); ALLOCATE(dBase(0))
879  ELSE
880    ALLOCATE(tdb(nb0-1)); tdb(1:nb0-1)=dBase(1:nb0-1); CALL MOVE_ALLOC(FROM=tdb, TO=dBase)
881  END IF
882  lerr = dispIsotopes(isot, 'Isotopes parameters read from file')
883
884END FUNCTION readIsotopesFile
885!==============================================================================================================================
886
887!==============================================================================================================================
888!=== IF ISOTOPES (2ND GENERATION TRACERS) ARE DETECTED:                                                                     ===
889!===    * COMPUTE MOST OF THE RELATED QUANTITIES ("isot" COMPONENTS).                                                       ===
890!===    * COMPUTE FEW ISOTOPES-DEDICATED "trac" COMPONENTS                                                                  ===
891!===    * CALL readIsotopesFile TO GET PHYSICAL QUANTITIES (<key>=<val> PAIRS)                                              ===
892!===      NOTE: THIS IS DONE HERE (IN A ROUTINE CALLED BY THE DYNAMIC), BECAUSE THE DYNAMIC NEEDS FEW PHYSICAL PARAMETERS.  ===
893!==============================================================================================================================
894SUBROUTINE initIsotopes(trac, isot)
895  TYPE(tra), ALLOCATABLE, TARGET, INTENT(INOUT) :: trac(:)
896  TYPE(iso), ALLOCATABLE, TARGET, INTENT(INOUT) :: isot(:)
897  CHARACTER(LEN=256), ALLOCATABLE :: p(:), str(:)                    !--- Temporary storage
898  CHARACTER(LEN=256) :: iname
899  CHARACTER(LEN=1)   :: ph                                           !--- Phase
900  INTEGER :: nbIso, ic, ip, iq, it, iz
901  LOGICAL, ALLOCATABLE :: ll(:)                                      !--- Mask
902  TYPE(tra), POINTER   ::  t(:), t1
903  TYPE(iso), POINTER   ::  s
904
905  t => trac
906
907  p = PACK(delPhase(t%prnt), MASK = t%type=='tracer' .AND. t%igen==2)!--- Parents of 2nd generation isotopes
908  CALL strReduce(p, nbIso)
909  ALLOCATE(isot(nbIso))
910
911  IF(nbIso==0) RETURN                                                !=== NO ISOTOPES: FINISHED
912
913  !--- ISOTOPES RELATED VARIABLES ; NULL OR EMPTY IF NO ISOTOPES
914  isot(:)%prnt = p
915  DO ic = 1, SIZE(p)                                                 !--- Loop on isotopes classes
916    s => isot(ic)
917    iname = s%prnt                                                   !--- Current isotopes class name (parent tracer name)
918
919    !=== Isotopes childs of tracer "iname": mask, names, number (same for each phase of "iname")
920    ll = t(:)%type=='tracer' .AND. delPhase(t(:)%prnt) == iname .AND. t(:)%phas == 'g'
921    str = PACK(delPhase(t(:)%name), MASK = ll)                       !--- Effectively found isotopes of "iname"
922    s%niso = SIZE(str)                                               !--- Number of "effectively found isotopes of "iname"
923    ALLOCATE(s%keys(s%niso))
924    FORALL(it = 1:s%niso) s%keys(it)%name = str(it)
925
926    !=== Geographic tagging tracers descending on tracer "iname": mask, names, number
927    ll = t(:)%type=='tag'    .AND. delPhase(t(:)%nam1) == iname .AND. t(:)%igen == 3
928    s%zone = PACK(strTail(t(:)%name,'_'), MASK = ll)                 !--- Tagging zones names  for isotopes category "iname"
929    CALL strReduce(s%zone)
930    s%nzon = SIZE(s%zone)                                            !--- Tagging zones number for isotopes category "iname"
931
932    !=== Geographic tracers of the isotopes childs of tracer "iname" (same for each phase of "iname")
933    !    NOTE: One might like to create a similar variable for 2nd generation tagging tracers (tagging the gen1 tracers)
934    str = PACK(delPhase(t(:)%name), MASK=ll)
935    CALL strReduce(str)
936    s%nitr = s%niso + SIZE(str)                                      !--- Number of isotopes + their geographic tracers [ntraciso]
937    ALLOCATE(s%trac(s%nitr))
938    FORALL(it = 1:s%niso) s%trac(it) = s%keys(it)%name
939    FORALL(it = s%niso+1:s%nitr) s%trac(it) = str(it-s%niso)
940
941    !=== Phases for tracer "iname"
942    s%phas = ''
943    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
944    s%npha = LEN_TRIM(s%phas)                                        !--- Equal to "nqo" for water
945
946    !=== Tables giving the index in a table of effectively found items for each dynamical tracer (1<=iq<=nqtot)
947    DO iq = 1, SIZE(t)
948      t1 => trac(iq)
949      IF(delPhase(t1%nam1) /= iname) CYCLE                            !--- Only deal with tracers descending on "iname"
950      t1%iso_igr = ic                                                 !--- Isotopes family       idx in list "isotopes(:)%prnt"
951      t1%iso_num = strIdx(s%trac, delPhase(strHead(t1%name,'_')))     !--- Current isotope       idx in effective isotopes list
952      t1%iso_zon = strIdx(s%zone,          strTail(t1%name,'_') )     !--- Current isotope zone  idx in effective zones    list
953      t1%iso_pha =  INDEX(s%phas,TRIM(t1%phas))                       !--- Current isotope phase idx in effective phases   list
954      IF(t1%igen /= 3) t1%iso_zon = 0                                 !--- Skip possible generation 2 tagging tracers
955    END DO
956
957    !=== Table used to get iq (index in dyn array, size nqtot) from the isotope and phase indexes ; the full isotopes list
958    !    (including tagging tracers) is sorted this way:  iso1, iso2, ..., iso1_zone1, iso2_zone1, ..., iso1_zoneN, iso2_zoneN
959    s%iTraPha = RESHAPE( [( (strIdx(t(:)%name,  addPhase(s%trac(it),s%phas(ip:ip))),     it=1, s%nitr), ip=1, s%npha)], &
960                         [s%nitr, s%npha] )
961
962    !=== Table used to get ix (index in tagging tracers isotopes list, size nitr) from the zone and isotope indexes
963    s%iZonIso = RESHAPE( [( (strIdx(s%trac(:), TRIM(s%trac(it))//'_'//TRIM(s%zone(iz))), iz=1, s%nzon), it=1, s%niso)], &
964                         [s%nzon, s%niso] )
965  END DO
966 
967  !=== Indexes, in dynamical tracers list, of the tracers transmitted to phytrac (nqtottr non-vanishing elements)
968  ll = delPhase(t%name)/='H2O' .AND. t%iso_num ==0              !--- Mask of tracers passed to the physics
969  t(:)%itr = UNPACK([(iq,iq=1,COUNT(ll))], ll, [(0, iq=1, SIZE(t))])
970
971  !=== READ PHYSICAL PARAMETERS FROM "isotopes_params.def" FILE
972  !    DONE HERE, AND NOT ONLY IN "infotrac_phy", BECAUSE SOME PHYSICAL PARAMS ARE NEEDED FOR RESTARTS (tnat AND alpha_ideal)
973  IF(readIsotopesFile('isotopes_params.def',isot)) CALL abort_gcm(modname,'Problem when reading isotopes parameters',1)
974
975END SUBROUTINE initIsotopes
976!==============================================================================================================================
977
978
979!==============================================================================================================================
980LOGICAL FUNCTION dispIsotopes(ides, message) RESULT(lerr)
981  TYPE(iso),        INTENT(IN) :: ides(:)                            !--- Isotopes descriptor vector
982  CHARACTER(LEN=*), INTENT(IN) :: message                            !--- Message to display
983  INTEGER :: ik, nk, ip, it, nt
984  CHARACTER(LEN=256) :: prf
985  CHARACTER(LEN=256), ALLOCATABLE :: ttl(:), val(:,:)
986  CALL msg(TRIM(message)//':')
987  DO ip = 1, SIZE(ides)                                              !--- Loop on parents tracers
988    nk = SIZE(ides(ip)%keys(1)%key)                                  !--- Same keys for each isotope
989    nt = SIZE(ides(ip)%keys)                                         !--- Number of isotopes
990    prf = 'i'//REPEAT('s',nk+1)                                      !--- Profile for table printing
991    ALLOCATE(ttl(nk+2), val(nt,nk+1))
992    ttl(1:2) = ['iq  ','name']; ttl(3:nk+2) = ides(ip)%keys(1)%key(:)!--- Titles line with keys names
993    val(:,1) = ides(ip)%keys(:)%name                                 !--- Values table 1st column: isotopes names 
994    DO ik = 1, nk
995      DO it = 1, nt
996        val(it,ik+1) = ides(ip)%keys(it)%val(ik)                     !--- Other columns: keys values
997      END DO
998    END DO
999    IF(test(fmsg(dispTable(prf, ttl, val, cat([(it,it=1,nt)]), rFmt='(EN8.4)'),'Problem with the table content'), lerr)) RETURN
1000    DEALLOCATE(ttl, val)
1001  END DO       
1002END FUNCTION dispIsotopes
1003!==============================================================================================================================
1004
1005
1006!==============================================================================================================================
1007SUBROUTINE addKey_1(key, val, ky, lOverWrite)
1008!------------------------------------------------------------------------------------------------------------------------------
1009! Purpose: Add the <key>=<val> pair in the "ky" keys descriptor.
1010!------------------------------------------------------------------------------------------------------------------------------
1011  CHARACTER(LEN=*),  INTENT(IN)    :: key, val
1012  TYPE(kys),         INTENT(INOUT) :: ky
1013  LOGICAL, OPTIONAL, INTENT(IN)    :: lOverWrite
1014  CHARACTER(LEN=256),  ALLOCATABLE :: k(:), v(:)
1015  INTEGER :: iky, nky
1016  LOGICAL :: lo
1017!------------------------------------------------------------------------------------------------------------------------------
1018  lo=.FALSE.; IF(PRESENT(lOverWrite)) lo=lOverWrite
1019  iky = strIdx(ky%key,key)
1020  IF(iky == 0) THEN
1021    nky = SIZE(ky%key)
1022    IF(nky == 0) THEN; ky%key = [key]; ky%val = [val]; ELSE; ky%key = [ky%key, key]; ky%val = [ky%val, val]; END IF
1023  ELSE IF(lo) THEN                                                   !--- Overwriting
1024    ky%key(iky) = key; ky%val(iky) = val
1025  END IF
1026END SUBROUTINE addKey_1
1027!==============================================================================================================================
1028SUBROUTINE addKey_m(key, val, ky, lOverWrite)
1029!------------------------------------------------------------------------------------------------------------------------------
1030! Purpose: Add the <key>=<val> pair in all the components of the "ky" keys descriptor.
1031!------------------------------------------------------------------------------------------------------------------------------
1032  CHARACTER(LEN=*),  INTENT(IN)    :: key, val
1033  TYPE(kys),         INTENT(INOUT) :: ky(:)
1034  LOGICAL, OPTIONAL, INTENT(IN)    :: lOverWrite
1035  INTEGER :: itr
1036  LOGICAL :: lo
1037!------------------------------------------------------------------------------------------------------------------------------
1038  lo=.FALSE.; IF(PRESENT(lOverWrite)) lo=lOverWrite
1039  DO itr = 1, SIZE(ky); CALL addKey_1(key, val, ky(itr), lo); END DO
1040END SUBROUTINE addKey_m
1041!==============================================================================================================================
1042SUBROUTINE addKeysFromDef(t, tr0)
1043!------------------------------------------------------------------------------------------------------------------------------
1044! Purpose: The values of the keys of the tracer named "tr0" are overwritten by the values found in the *.def files, if any.
1045!------------------------------------------------------------------------------------------------------------------------------
1046  TYPE(tra), ALLOCATABLE, INTENT(INOUT) :: t(:)
1047  CHARACTER(LEN=*),       INTENT(IN)    :: tr0
1048  CHARACTER(LEN=256) :: val
1049  INTEGER            :: ik, jd
1050  jd = strIdx(t%name, tr0)
1051  IF(jd == 0) RETURN
1052  DO ik = 1, SIZE(t(jd)%keys%key)
1053    CALL get_in(t(jd)%keys%key(ik), val, 'zzzz')
1054    IF(val /= 'zzzz') CALL addKey_1(t(jd)%keys%key(ik), val, t(jd)%keys, .TRUE.)
1055  END DO
1056END SUBROUTINE addKeysFromDef
1057!==============================================================================================================================
1058SUBROUTINE delKey_1(itr, keyn, ky)
1059!------------------------------------------------------------------------------------------------------------------------------
1060! Purpose: Internal routine.
1061!   Remove <key>=<val> pairs in the "itr"th component of the "ky" keys descriptor.
1062!------------------------------------------------------------------------------------------------------------------------------
1063  INTEGER,          INTENT(IN)    :: itr
1064  CHARACTER(LEN=*), INTENT(IN)    :: keyn(:)
1065  TYPE(tra),        INTENT(INOUT) :: ky(:)
1066  CHARACTER(LEN=256), ALLOCATABLE :: k(:), v(:)
1067  LOGICAL,  ALLOCATABLE :: ll(:)
1068  INTEGER :: iky
1069!------------------------------------------------------------------------------------------------------------------------------
1070  IF(itr<=0 .OR. itr>SIZE(ky, DIM=1)) RETURN                          !--- Index is out of range
1071  ll = [( ALL(keyn/=ky(itr)%keys%key(iky)), iky=1, SIZE(ky(itr)%keys%key) )]
1072  k = PACK(ky(itr)%keys%key, MASK=ll); CALL MOVE_ALLOC(FROM=k, TO=ky(itr)%keys%key)
1073  v = PACK(ky(itr)%keys%val, MASK=ll); CALL MOVE_ALLOC(FROM=v, TO=ky(itr)%keys%val)
1074END SUBROUTINE delKey_1
1075!==============================================================================================================================
1076SUBROUTINE delKey(keyn, ky)
1077!------------------------------------------------------------------------------------------------------------------------------
1078! Purpose: Internal routine.
1079!   Remove <key>=<val> pairs in all the components of the "t" tracers descriptor.
1080!------------------------------------------------------------------------------------------------------------------------------
1081  CHARACTER(LEN=*), INTENT(IN)    :: keyn(:)
1082  TYPE(tra),        INTENT(INOUT) :: ky(:)
1083  INTEGER :: iky
1084!------------------------------------------------------------------------------------------------------------------------------
1085  DO iky = 1, SIZE(ky); CALL delKey_1(iky, keyn, ky); END DO
1086END SUBROUTINE delKey
1087!==============================================================================================================================
1088
1089
1090!==============================================================================================================================
1091!=== PUBLIC ROUTINES: GET A KEY FROM A <key>=<val> LIST ; VECTORS, TRACER AND DATABASE VERSIONS ===============================
1092!=== BEWARE !!! IF THE "ky" ARGUMENT IS NOT PRESENT, THEN THE VARIABLES "tracers" AND "isotopes" ARE USED. ====================
1093!===     THEY ARE LOCAL TO THIS MODULE, SO MUST MUST BE INITIALIZED FIRST USING the "getKey_init" ROUTINE  ====================
1094!==============================================================================================================================
1095SUBROUTINE getKey_init(tracers_, isotopes_)
1096  TYPE(tra), OPTIONAL, INTENT(IN) ::  tracers_(:)
1097  TYPE(iso), OPTIONAL, INTENT(IN) :: isotopes_(:)
1098  IF(PRESENT( tracers_))  tracers =  tracers_
1099  IF(PRESENT(isotopes_)) isotopes = isotopes_
1100END SUBROUTINE getKey_init
1101!==============================================================================================================================
1102CHARACTER(LEN=256) FUNCTION fgetKey(itr, keyn, ky, def_val) RESULT(out)
1103!------------------------------------------------------------------------------------------------------------------------------
1104! Purpose: Internal function ; get a key value in string format (this is the returned argument).
1105!------------------------------------------------------------------------------------------------------------------------------
1106  INTEGER,                    INTENT(IN) :: itr
1107  CHARACTER(LEN=*),           INTENT(IN) :: keyn
1108  TYPE(kys),                  INTENT(IN) :: ky(:)
1109  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: def_val
1110!------------------------------------------------------------------------------------------------------------------------------
1111  INTEGER :: ik
1112  ik = 0; IF(itr>0 .AND. itr<=SIZE(ky)) ik = strIdx(ky(itr)%key(:), keyn)
1113  out = '';              IF(ik /= 0) out = ky(itr)%val(ik)           !--- Key was found
1114  IF(PRESENT(def_val) .AND. ik == 0) out = def_val                   !--- Default value from arguments
1115END FUNCTION fgetKey
1116!==============================================================================================================================
1117LOGICAL FUNCTION getKeyByName_s1(keyn, val, tname, ky) RESULT(lerr)
1118  !--- Purpose: Get the value of the key named "keyn" for the tracer named "tnam".
1119  !     * "ky" unspecified: try in "tracers" for "tnam" with phase and tagging suffixes, then in "isotopes" without.
1120  !     * "ky"   specified: try in "ky"      for "tnam" with phase and tagging suffixes, then without.
1121  !    The returned error code is always .FALSE.: an empty string is returned when the key hasn't been found.
1122  CHARACTER(LEN=*),    INTENT(IN)  :: keyn
1123  CHARACTER(LEN=256),  INTENT(OUT) :: val
1124  CHARACTER(LEN=*),    INTENT(IN)  :: tname
1125  TYPE(kys), OPTIONAL, INTENT(IN)  :: ky(:)
1126  INTEGER :: is
1127  lerr = .FALSE.
1128  IF(PRESENT(ky)) THEN
1129    val = getKeyByName_prv(keyn, tname , ky);    IF(val /= '') RETURN          !--- "ky" and "tnam"
1130    val = getKeyByName_prv(keyn, delPhase(strHead(tname,'_')), ky)             !--- "ky" and "tnam" without phase
1131  ELSE
1132    IF(.NOT.ALLOCATED(tracers))  RETURN
1133    val = getKeyByName_prv(keyn, tname, tracers(:)%keys); IF(val /= '') RETURN !--- "tracers" and "tnam"
1134    IF(.NOT.ALLOCATED(isotopes)) RETURN
1135    IF(SIZE(isotopes) == 0)      RETURN
1136    DO is = 1, SIZE(isotopes); IF(strIdx(isotopes(is)%keys(:)%name, delPhase(strHead(tname,'_'))) /= 0) EXIT; END DO
1137    IF(is /= 0) val = getKeyByName_prv(keyn, tname, isotopes(is)%keys(:))      !--- "isotopes" and "tnam" without phase
1138  END IF
1139
1140CONTAINS
1141
1142FUNCTION getKeyByName_prv(keyn, tname, ky) RESULT(val)
1143  CHARACTER(LEN=256)            :: val
1144  CHARACTER(LEN=*), INTENT(IN)  :: keyn
1145  CHARACTER(LEN=*), INTENT(IN)  :: tname
1146  TYPE(kys),        INTENT(IN)  :: ky(:)
1147  INTEGER :: itr, iky
1148  val = ''; iky = 0
1149  itr = strIdx(ky(:)%name, tname);                 IF(itr==0) RETURN           !--- Get the index of the wanted tracer
1150  IF(itr /= 0) iky = strIdx(ky(itr)%key(:), keyn); IF(iky==0) RETURN           !--- Wanted key    index
1151  val = ky(itr)%val(iky)
1152END FUNCTION getKeyByName_prv
1153
1154END FUNCTION getKeyByName_s1
1155!==============================================================================================================================
1156LOGICAL FUNCTION getKeyByName_sm(keyn, val, tnam, ky) RESULT(lerr)
1157  CHARACTER(LEN=*),                   INTENT(IN)  :: keyn
1158  CHARACTER(LEN=256),    ALLOCATABLE, INTENT(OUT) ::  val(:)
1159  CHARACTER(LEN=*), TARGET, OPTIONAL, INTENT(IN)  :: tnam(:)
1160  TYPE(kys),        TARGET, OPTIONAL, INTENT(IN)  ::   ky(:)
1161  CHARACTER(LEN=256), POINTER :: n(:)
1162  INTEGER :: iq
1163  n => tracers(:)%keys%name; IF(PRESENT(tnam)) n => tnam(:)
1164  ALLOCATE(val(SIZE(n)))
1165  IF(     PRESENT(ky)) lerr = ANY([(getKeyByName_s1(keyn, val(iq), n(iq), ky), iq=1, SIZE(n))])
1166  IF(.NOT.PRESENT(ky)) lerr = ANY([(getKeyByName_s1(keyn, val(iq), n(iq)),     iq=1, SIZE(n))])
1167END FUNCTION getKeyByName_sm
1168!==============================================================================================================================
1169LOGICAL FUNCTION getKeyByName_i1(keyn, val, tnam, ky) RESULT(lerr)
1170  CHARACTER(LEN=*),    INTENT(IN)  :: keyn
1171  INTEGER,             INTENT(OUT) :: val
1172  CHARACTER(LEN=*),    INTENT(IN)  :: tnam
1173  TYPE(kys), OPTIONAL, INTENT(IN)  :: ky(:)
1174  CHARACTER(LEN=256) :: sval
1175  INTEGER :: ierr
1176  IF(     PRESENT(ky)) lerr = getKeyByName_s1(keyn, sval, tnam, ky)
1177  IF(.NOT.PRESENT(ky)) lerr = getKeyByName_s1(keyn, sval, tnam)
1178  IF(test(fmsg(lerr,   'key "'//TRIM(keyn)//'" or tracer "'//TRIM(tnam)//'" is missing'),        lerr)) RETURN
1179  READ(sval, *, IOSTAT=ierr) val
1180  IF(test(fmsg(ierr/=0,'key "'//TRIM(keyn)//'" of tracer "'//TRIM(tnam)//'" is not an integer'), lerr)) RETURN
1181END FUNCTION getKeyByName_i1
1182!==============================================================================================================================
1183LOGICAL FUNCTION getKeyByName_im(keyn, val, tnam, ky) RESULT(lerr)
1184  CHARACTER(LEN=*),                   INTENT(IN)  :: keyn
1185  INTEGER,               ALLOCATABLE, INTENT(OUT) ::  val(:)
1186  CHARACTER(LEN=*), TARGET, OPTIONAL, INTENT(IN)  :: tnam(:)
1187  TYPE(kys),        TARGET, OPTIONAL, INTENT(IN)  ::   ky(:)
1188  CHARACTER(LEN=256), POINTER :: n(:)
1189  INTEGER :: iq
1190  n => tracers(:)%name; IF(PRESENT(tnam)) n => tnam(:)
1191  ALLOCATE(val(SIZE(n)))
1192  IF(     PRESENT(ky)) lerr = ANY([(getKeyByName_i1(keyn, val(iq), n(iq), ky), iq=1, SIZE(n))])
1193  IF(.NOT.PRESENT(ky)) lerr = ANY([(getKeyByName_i1(keyn, val(iq), n(iq)),     iq=1, SIZE(n))])
1194END FUNCTION getKeyByName_im
1195!==============================================================================================================================
1196LOGICAL FUNCTION getKeyByName_r1(keyn, val, tnam, ky) RESULT(lerr)
1197  CHARACTER(LEN=*),    INTENT(IN)  :: keyn
1198  REAL,                INTENT(OUT) :: val
1199  CHARACTER(LEN=*),    INTENT(IN)  :: tnam
1200  TYPE(kys), OPTIONAL, INTENT(IN)  :: ky(:)
1201  CHARACTER(LEN=256) :: sval
1202  INTEGER :: ierr
1203  IF(     PRESENT(ky)) lerr = getKeyByName_s1(keyn, sval, tnam, ky)
1204  IF(.NOT.PRESENT(ky)) lerr = getKeyByName_s1(keyn, sval, tnam)
1205  IF(test(fmsg(lerr,   'key "'//TRIM(keyn)//'" or tracer "'//TRIM(tnam)//'" is missing'),    lerr)) RETURN
1206  READ(sval, *, IOSTAT=ierr) val
1207  IF(test(fmsg(ierr/=0,'key "'//TRIM(keyn)//'" of tracer "'//TRIM(tnam)//'" is not a real'), lerr)) RETURN
1208END FUNCTION getKeyByName_r1
1209!==============================================================================================================================
1210LOGICAL FUNCTION getKeyByName_rm(keyn, val, tnam, ky) RESULT(lerr)
1211  CHARACTER(LEN=*),                   INTENT(IN)  :: keyn
1212  REAL,                  ALLOCATABLE, INTENT(OUT) ::  val(:)
1213  CHARACTER(LEN=*), TARGET, OPTIONAL, INTENT(IN)  :: tnam(:)
1214  TYPE(kys),        TARGET, OPTIONAL, INTENT(IN)  ::   ky(:)
1215  CHARACTER(LEN=256), POINTER :: n(:)
1216  INTEGER :: iq
1217  n => tracers(:)%name;  IF(PRESENT(tnam)) n => tnam(:)
1218  ALLOCATE(val(SIZE(n)))
1219  IF(     PRESENT(ky)) lerr = ANY([(getKeyByName_r1(keyn, val(iq), n(iq), ky), iq=1, SIZE(n))])
1220  IF(.NOT.PRESENT(ky)) lerr = ANY([(getKeyByName_r1(keyn, val(iq), n(iq)),     iq=1, SIZE(n))])
1221END FUNCTION getKeyByName_rm
1222!==============================================================================================================================
1223
1224
1225!==============================================================================================================================
1226!=== REMOVE, IF ANY, OR ADD THE PHASE SUFFIX OF THE TRACER NAMED "s" ==========================================================
1227!==============================================================================================================================
1228ELEMENTAL CHARACTER(LEN=256) FUNCTION delPhase(s) RESULT(out)
1229  CHARACTER(LEN=*), INTENT(IN) :: s
1230  INTEGER :: l, i
1231  out = s
1232  IF(s == '') RETURN
1233  i = INDEX(s, '_'); l = LEN_TRIM(s)
1234  IF(i == 0) THEN
1235    IF(s(l-1:l-1)==phases_sep .AND. INDEX(known_phases,s(l:l)) /= 0) out = s(1:l-2)
1236  ELSE; i=i-1
1237    IF(s(i-1:i-1)==phases_sep .AND. INDEX(known_phases,s(i:i)) /= 0) out = s(1:i-2)//s(i+1:l)
1238  END IF
1239END FUNCTION delPhase
1240!------------------------------------------------------------------------------------------------------------------------------
1241CHARACTER(LEN=256) FUNCTION addPhase_1(s,pha) RESULT(out)
1242  CHARACTER(LEN=*), INTENT(IN) :: s
1243  CHARACTER(LEN=1), INTENT(IN) :: pha
1244  INTEGER :: l, i
1245  out = s
1246  IF(s == '') RETURN
1247  i = INDEX(s, '_'); l = LEN_TRIM(s)
1248  IF(i == 0) out =  TRIM(s)//phases_sep//pha
1249  IF(i /= 0) out = s(1:i-1)//phases_sep//pha//'_'//s(i+1:l)
1250END FUNCTION addPhase_1
1251!------------------------------------------------------------------------------------------------------------------------------
1252FUNCTION addPhase_m(s,pha) RESULT(out)
1253  CHARACTER(LEN=*),    INTENT(IN) :: s(:)
1254  CHARACTER(LEN=1),    INTENT(IN) :: pha
1255  CHARACTER(LEN=256), ALLOCATABLE :: out(:) 
1256  INTEGER :: k
1257  out = [( addPhase_1(s(k), pha), k=1, SIZE(s) )]
1258END FUNCTION addPhase_m
1259!------------------------------------------------------------------------------------------------------------------------------
1260
1261
1262!==============================================================================================================================
1263!=== GET THE NAME(S) OF THE ANCESTOR(S) OF TRACER(S) "tname" AT GENERATION "igen"  IN THE TRACERS DESCRIPTORS LIST "tr" =======
1264!==============================================================================================================================
1265CHARACTER(LEN=256) FUNCTION ancestor_1(t, tname, igen) RESULT(out)
1266  TYPE(tra),         INTENT(IN) :: t(:)
1267  CHARACTER(LEN=*),  INTENT(IN) :: tname
1268  INTEGER, OPTIONAL, INTENT(IN) :: igen
1269  INTEGER :: ig, ix
1270  ig = 1; IF(PRESENT(igen)) ig = igen
1271  ix = idxAncestor_1(t, tname, ig)
1272  out = ''; IF(ix /= 0) out = t(ix)%name
1273END FUNCTION ancestor_1
1274!------------------------------------------------------------------------------------------------------------------------------
1275FUNCTION ancestor_m(t, tname, igen) RESULT(out)
1276  CHARACTER(LEN=256), ALLOCATABLE        ::   out(:)
1277  TYPE(tra),                  INTENT(IN) ::     t(:)
1278  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: tname(:)
1279  INTEGER,          OPTIONAL, INTENT(IN) :: igen
1280  INTEGER, ALLOCATABLE :: ix(:)
1281  INTEGER :: ig
1282  ig = 1; IF(PRESENT(igen)) ig = igen
1283  IF(     PRESENT(tname)) ix = idxAncestor_m(t, tname,     ig)
1284  IF(.NOT.PRESENT(tname)) ix = idxAncestor_m(t, t(:)%name, ig)
1285  ALLOCATE(out(SIZE(ix))); out(:) = ''
1286  WHERE(ix /= 0) out = t(ix)%name
1287END FUNCTION ancestor_m
1288!==============================================================================================================================
1289
1290
1291!==============================================================================================================================
1292!=== GET THE INDEX(ES) OF THE ANCESTOR(S) OF TRACER(S) "tname" AT GENERATION "igen"  IN THE TRACERS DESCRIPTORS LIST "tr" =====
1293!==============================================================================================================================
1294INTEGER FUNCTION idxAncestor_1(t, tname, igen) RESULT(out)
1295! Return the name of the generation "igen" ancestor of "tname"
1296  TYPE(tra),         INTENT(IN) :: t(:)
1297  CHARACTER(LEN=*),  INTENT(IN) :: tname
1298  INTEGER, OPTIONAL, INTENT(IN) :: igen
1299  INTEGER :: ig
1300  ig = 1; IF(PRESENT(igen)) ig = igen
1301  out = strIdx(t(:)%name, tname)
1302  IF(out == 0)          RETURN
1303  IF(t(out)%igen <= ig) RETURN
1304  DO WHILE(t(out)%igen > ig); out = strIdx(t(:)%name, t(out)%prnt); END DO
1305END FUNCTION idxAncestor_1
1306!------------------------------------------------------------------------------------------------------------------------------
1307FUNCTION idxAncestor_m(t, tname, igen) RESULT(out)
1308  INTEGER,          ALLOCATABLE          ::   out(:)
1309  TYPE(tra),                  INTENT(IN) ::     t(:)
1310  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: tname(:)
1311  INTEGER,          OPTIONAL, INTENT(IN) :: igen
1312  INTEGER :: ig, ix
1313  ig = 1; IF(PRESENT(igen)) ig = igen
1314  IF(     PRESENT(tname)) out = [(idxAncestor_1(t, tname(ix),  ig), ix=1, SIZE(tname))]
1315  IF(.NOT.PRESENT(tname)) out = [(idxAncestor_1(t, t(ix)%name, ig), ix=1, SIZE(t))]
1316END FUNCTION idxAncestor_m
1317!==============================================================================================================================
1318
1319
1320END MODULE readTracFiles_mod
Note: See TracBrowser for help on using the repository browser.