GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: misc/readTracFiles_mod.f90 Lines: 382 832 45.9 %
Date: 2023-06-30 12:51:15 Branches: 881 4070 21.6 %

Line Branch Exec Source
1
MODULE readTracFiles_mod
2
3
  USE strings_mod,    ONLY: msg, find, get_in, str2int, dispTable, strHead,  strReduce,  strFind, strStack, strIdx, &
4
       test, removeComment, cat, fmsg, maxlen, int2str, checkList, strParse, strReplace, strTail, strCount, reduceExpr
5
6
  IMPLICIT NONE
7
8
  PRIVATE
9
10
  PUBLIC :: maxlen                                              !--- PARAMETER FOR CASUAL STRING LENGTH
11
  PUBLIC :: tracers                                             !--- TRACERS  DESCRIPTION DATABASE
12
  PUBLIC :: trac_type, setGeneration, indexUpdate               !--- TRACERS  DESCRIPTION ASSOCIATED TOOLS
13
  PUBLIC :: testTracersFiles, readTracersFiles                  !--- TRACERS FILES READING ROUTINES
14
  PUBLIC :: getKey, fGetKey, fGetKeys, addKey, setDirectKeys    !--- TOOLS TO GET/SET KEYS FROM/TO  tracers & isotopes
15
  PUBLIC :: getKeysDBase,    setKeysDBase                       !--- TOOLS TO GET/SET THE DATABASE (tracers & isotopes)
16
17
  PUBLIC :: addPhase, getiPhase,  old_phases, phases_sep, &     !--- FUNCTIONS RELATED TO THE PHASES
18
   nphases, delPhase, getPhase, known_phases, phases_names      !--- + ASSOCIATED VARIABLES
19
20
  PUBLIC :: oldH2OIso, newH2OIso, old2newH2O, new2oldH2O        !--- H2O ISOTOPES BACKWARD COMPATIBILITY (OLD traceur.def)
21
  PUBLIC :: oldHNO3,   newHNO3                                  !--- HNO3 REPRO   BACKWARD COMPATIBILITY (OLD start.nc)
22
23
  PUBLIC :: tran0, idxAncestor, ancestor                        !--- GENERATION 0 TRACER + TOOLS FOR GENERATIONS
24
25
  !=== FOR ISOTOPES: GENERAL
26
  PUBLIC :: isot_type, readIsotopesFile, isoSelect              !--- ISOTOPES DESCRIPTION TYPE + READING ROUTINE
27
  PUBLIC :: ixIso, nbIso                                        !--- INDEX OF SELECTED ISOTOPES CLASS + NUMBER OF CLASSES
28
29
  !=== FOR ISOTOPES: H2O FAMILY ONLY
30
  PUBLIC :: iH2O
31
32
  !=== FOR ISOTOPES: DEPENDING ON THE SELECTED ISOTOPES CLASS
33
  PUBLIC :: isotope, isoKeys                                    !--- SELECTED ISOTOPES DATABASE + ASSOCIATED KEYS
34
  PUBLIC :: isoName, isoZone, isoPhas                           !--- ISOTOPES AND TAGGING ZONES NAMES AND PHASES
35
  PUBLIC :: niso,    nzone,   nphas,   ntiso                    !---  " " NUMBERS + ISOTOPES AND TAGGING TRACERS NUMBERS
36
  PUBLIC :: itZonIso                                            !--- Idx IN isoName(1:niso) = f(tagging idx, isotope idx)
37
  PUBLIC :: iqIsoPha                                            !--- Idx IN qx(1:nqtot)     = f(isotope idx,   phase idx)
38
  PUBLIC :: isoCheck                                            !--- FLAG TO RUN ISOTOPES CHECKING ROUTINES
39
40
  PUBLIC :: maxTableWidth
41
!------------------------------------------------------------------------------------------------------------------------------
42
  TYPE :: keys_type                                        !=== TYPE FOR A SET OF KEYS ASSOCIATED TO AN ELEMENT
43
    CHARACTER(LEN=maxlen)              :: name             !--- Tracer name
44
    CHARACTER(LEN=maxlen), ALLOCATABLE :: key(:)           !--- Keys string list
45
    CHARACTER(LEN=maxlen), ALLOCATABLE :: val(:)           !--- Corresponding values string list
46
  END TYPE keys_type
47
!------------------------------------------------------------------------------------------------------------------------------
48
  TYPE :: trac_type                                        !=== TYPE FOR A SINGLE TRACER NAMED "name"
49
    CHARACTER(LEN=maxlen) :: name        = ''              !--- Name of the tracer
50
    CHARACTER(LEN=maxlen) :: gen0Name    = ''              !--- First generation ancestor name
51
    CHARACTER(LEN=maxlen) :: parent      = ''              !--- Parent name
52
    CHARACTER(LEN=maxlen) :: longName    = ''              !--- Long name (with advection scheme suffix)
53
    CHARACTER(LEN=maxlen) :: type        = 'tracer'        !--- Type  (so far: 'tracer' / 'tag')
54
    CHARACTER(LEN=maxlen) :: phase       = 'g'             !--- Phase ('g'as / 'l'iquid / 's'olid)
55
    CHARACTER(LEN=maxlen) :: component   = ''              !--- Coma-separated list of components (Ex: lmdz,inca)
56
    INTEGER               :: iGeneration = -1              !--- Generation number (>=0)
57
    INTEGER               :: iqParent    = 0               !--- Parent index
58
    INTEGER,  ALLOCATABLE :: iqDescen(:)                   !--- Descendants index (in growing generation order)
59
    INTEGER               :: nqDescen    = 0               !--- Number of descendants (all generations)
60
    INTEGER               :: nqChildren  = 0               !--- Number of children  (first generation)
61
    TYPE(keys_type)       :: keys                          !--- <key>=<val> pairs vector
62
    INTEGER               :: iadv        = 10              !--- Advection scheme used
63
    LOGICAL               :: isAdvected  = .FALSE.         !--- "true" tracers: iadv > 0.   COUNT(isAdvected )=nqtrue
64
    LOGICAL               :: isInPhysics = .TRUE.          !--- "true" tracers: in tr_seri. COUNT(isInPhysics)=nqtottr
65
    INTEGER               :: iso_iGroup  = 0               !--- Isotopes group index in isotopes(:)
66
    INTEGER               :: iso_iName   = 0               !--- Isotope  name  index in isotopes(iso_iGroup)%trac(:)
67
    INTEGER               :: iso_iZone   = 0               !--- Isotope  zone  index in isotopes(iso_iGroup)%zone(:)
68
    INTEGER               :: iso_iPhase  = 0               !--- Isotope  phase index in isotopes(iso_iGroup)%phase
69
  END TYPE trac_type
70
!------------------------------------------------------------------------------------------------------------------------------
71
  TYPE :: isot_type                                        !=== TYPE FOR AN ISOTOPES FAMILY DESCENDING ON TRACER "parent"
72
    CHARACTER(LEN=maxlen)              :: parent           !--- Isotopes family name (parent tracer name ; ex: H2O)
73
    LOGICAL                            :: check=.FALSE.    !--- Triggering of the checking routines
74
    TYPE(keys_type),       ALLOCATABLE :: keys(:)          !--- Isotopes keys/values pairs list     (length: niso)
75
    CHARACTER(LEN=maxlen), ALLOCATABLE :: trac(:)          !--- Isotopes + tagging tracers list     (length: ntiso)
76
    CHARACTER(LEN=maxlen), ALLOCATABLE :: zone(:)          !--- Geographic tagging zones names list (length: nzone)
77
    CHARACTER(LEN=maxlen)              :: phase = 'g'      !--- Phases list: [g][l][s]              (length: nphas)
78
    INTEGER                            :: niso  = 0        !--- Number of isotopes, excluding tagging tracers
79
    INTEGER                            :: nzone = 0        !--- Number of geographic tagging zones
80
    INTEGER                            :: ntiso = 0        !--- Number of isotopes, including tagging tracers
81
    INTEGER                            :: nphas = 0        !--- Number phases
82
    INTEGER,               ALLOCATABLE :: iqIsoPha(:,:)    !--- Idx in "tracers(1:nqtot)" = f(name(1:ntiso)),phas)
83
                                                           !---        "iqIsoPha" former name: "iqiso"
84
    INTEGER,               ALLOCATABLE :: itZonIso(:,:)    !--- Idx in "trac(1:ntiso)" = f(zone, name(1:niso))
85
                                                           !---        "itZonIso" former name: "index_trac"
86
  END TYPE isot_type
87
!------------------------------------------------------------------------------------------------------------------------------
88
  TYPE :: dataBase_type                                         !=== TYPE FOR TRACERS SECTION
89
    CHARACTER(LEN=maxlen)  :: name                              !--- Section name
90
    TYPE(trac_type), ALLOCATABLE :: trac(:)                     !--- Tracers descriptors
91
  END TYPE dataBase_type
92
!------------------------------------------------------------------------------------------------------------------------------
93
  INTERFACE getKey
94
    MODULE PROCEDURE getKeyByName_s1, getKeyByName_s1m, getKeyByName_sm, getKey_sm, &
95
                     getKeyByName_i1, getKeyByName_i1m, getKeyByName_im, getKey_im, &
96
                     getKeyByName_r1, getKeyByName_r1m, getKeyByName_rm, getKey_rm, &
97
                     getKeyByName_l1, getKeyByName_l1m, getKeyByName_lm, getKey_lm
98
  END INTERFACE getKey
99
!------------------------------------------------------------------------------------------------------------------------------
100
  INTERFACE    isoSelect;  MODULE PROCEDURE  isoSelectByIndex,  isoSelectByName; END INTERFACE isoSelect
101
  INTERFACE  old2newH2O;   MODULE PROCEDURE  old2newH2O_1,  old2newH2O_m;        END INTERFACE old2newH2O
102
  INTERFACE  new2oldH2O;   MODULE PROCEDURE  new2oldH2O_1,  new2oldH2O_m;        END INTERFACE new2oldH2O
103
  INTERFACE fGetKey;       MODULE PROCEDURE fgetKeyIdx_s1, fgetKeyNam_s1;        END INTERFACE fGetKey
104
  INTERFACE tracersSubset; MODULE PROCEDURE trSubset_Indx, trSubset_Name, trSubset_gen0Name; END INTERFACE tracersSubset
105
  INTERFACE idxAncestor;   MODULE PROCEDURE idxAncestor_1, idxAncestor_m, idxAncestor_mt;    END INTERFACE idxAncestor
106
  INTERFACE    ancestor;   MODULE PROCEDURE    ancestor_1,    ancestor_m,    ancestor_mt;    END INTERFACE    ancestor
107
  INTERFACE      addKey;   MODULE PROCEDURE      addKey_1; END INTERFACE addKey!,      addKey_m,     addKey_mm;     END INTERFACE addKey
108
  INTERFACE    addPhase;   MODULE PROCEDURE   addPhase_s1,   addPhase_sm,   addPhase_i1,   addPhase_im; END INTERFACE addPhase
109
!------------------------------------------------------------------------------------------------------------------------------
110
111
  !=== MAIN DATABASE: files sections descriptors
112
  TYPE(dataBase_type), SAVE, ALLOCATABLE, TARGET :: dBase(:)
113
114
  !--- SOME PARAMETERS THAT ARE NOT LIKELY TO CHANGE OFTEN
115
  CHARACTER(LEN=maxlen), SAVE      :: tran0        = 'air'      !--- Default transporting fluid
116
  CHARACTER(LEN=maxlen), PARAMETER :: old_phases   = 'vlirb'     !--- Old phases for water (no separator)
117
  CHARACTER(LEN=maxlen), PARAMETER :: known_phases = 'glsrb'     !--- Known phases initials
118
  INTEGER, PARAMETER :: nphases = LEN_TRIM(known_phases)        !--- Number of phases
119
  CHARACTER(LEN=maxlen), SAVE      :: phases_names(nphases) &   !--- Known phases names
120
                                = ['gaseous', 'liquid ', 'solid  ', 'cloud  ','blosno ']
121
  CHARACTER(LEN=1), SAVE :: phases_sep  =  '_'                  !--- Phase separator
122
  LOGICAL,          SAVE :: tracs_merge = .TRUE.                !--- Merge/stack tracers lists
123
  LOGICAL,          SAVE :: lSortByGen  = .TRUE.                !--- Sort by growing generation
124
  CHARACTER(LEN=maxlen), SAVE :: isoFile = 'isotopes_params.def'!--- Name of the isotopes parameters file
125
126
  !--- CORRESPONDANCE BETWEEN OLD AND NEW WATER NAMES
127
  CHARACTER(LEN=maxlen), SAVE :: oldH2OIso(5) = ['eau',   'HDO',   'O18',   'O17',   'HTO'  ]
128
  CHARACTER(LEN=maxlen), SAVE :: newH2OIso(5) = ['H216O', 'HDO  ', 'H218O', 'H217O', 'HTO  ']
129
130
  !--- CORRESPONDANCE BETWEEN OLD AND NEW HNO3 RELATED SPECIES NAMES
131
  CHARACTER(LEN=maxlen), SAVE ::   oldHNO3(2) = ['HNO3_g ', 'HNO3   ']
132
  CHARACTER(LEN=maxlen), SAVE ::   newHNO3(2) = ['HNO3   ', 'HNO3tot']
133
134
  !=== TRACERS AND ISOTOPES DESCRIPTORS, USED BY getKey
135
  TYPE(trac_type), ALLOCATABLE, TARGET, SAVE ::  tracers(:)
136
  TYPE(isot_type), ALLOCATABLE, TARGET, SAVE :: isotopes(:)
137
138
  !=== ALIASES OF VARIABLES FROM SELECTED ISOTOPES FAMILY EMBEDDED IN "isotope" (isotopes(ixIso))
139
  TYPE(isot_type),         SAVE, POINTER :: isotope             !--- CURRENTLY SELECTED ISOTOPES FAMILY DESCRIPTOR
140
  INTEGER,                 SAVE          :: ixIso, iH2O         !--- Index of the selected isotopes family and H2O family
141
  INTEGER,                 SAVE          :: nbIso               !--- Number of isotopes classes
142
  LOGICAL,                 SAVE          :: isoCheck            !--- Flag to trigger the checking routines
143
  TYPE(keys_type),         SAVE, POINTER :: isoKeys(:)          !--- ONE SET OF KEYS FOR EACH ISOTOPE (LISTED IN isoName)
144
  CHARACTER(LEN=maxlen),   SAVE, POINTER :: isoName(:),   &     !--- ISOTOPES NAMES FOR THE CURRENTLY SELECTED FAMILY
145
                                            isoZone(:),   &     !--- TAGGING ZONES  FOR THE CURRENTLY SELECTED FAMILY
146
                                            isoPhas             !--- USED PHASES    FOR THE CURRENTLY SELECTED FAMILY
147
  INTEGER,                 SAVE          ::  niso, nzone, &     !--- NUMBER OF ISOTOPES, TAGGING ZONES AND PHASES
148
                                            nphas, ntiso        !--- NUMBER OF PHASES AND ISOTOPES + ISOTOPIC TAGGING TRACERS
149
  INTEGER,                 SAVE, POINTER ::itZonIso(:,:), &     !--- INDEX IN "isoTrac" AS f(tagging zone idx,  isotope idx)
150
                                           iqIsoPha(:,:)        !--- INDEX IN "qx"      AS f(isotopic tracer idx, phase idx)
151
152
  INTEGER,    PARAMETER :: maxTableWidth = 192                  !--- Maximum width of a table displayed with "dispTable"
153
  CHARACTER(LEN=maxlen) :: modname
154
155
CONTAINS
156
157
!==============================================================================================================================
158
!==============================================================================================================================
159
!=== READ ONE OR SEVERAL TRACER FILES AND FILL A "tr" TRACERS DESCRIPTOR DERIVED TYPE.
160
!=== THE RETURNED VALUE fType DEPENDS ON WHAT IS FOUND:
161
!===  0: NO ADEQUATE FILE FOUND ; DEFAULT VALUES MUST BE USED
162
!===  1: AN "OLD STYLE" TRACERS FILE "traceur.def":
163
!===    First line: <nb tracers>     Other lines: <hadv> <vadv> <tracer name> [<parent name>]
164
!===  2: A  "NEW STYLE" TRACERS FILE  "tracer.def" WITH SEVERAL SECTIONS.
165
!===  3: SEVERAL  "  "  TRACERS FILES "tracer_<component>.def" WITH A SINGLE SECTION IN EACH.
166
!=== REMARKS:
167
!===  * EACH SECTION BEGINS WITH A "&<section name> LINE
168
!===  * DEFAULT VALUES FOR ALL THE SECTIONS OF THE FILE ARE DEFINED IN THE SPECIAL SECTION "&default"
169
!===  * EACH SECTION LINE HAS THE STRUCTURE:  <name(s)>  <key1>=<value1> <key2>=<value2> ...
170
!===  * SO FAR, THE DEFINED KEYS ARE: parent, phases, hadv, vadv, type
171
!===  * <name> AND <parent> CAN BE LISTS OF COMA-SEPARATED TRACERS ; THE ROUTINE EXPAND THESE FACTORIZATIONS.
172
!=== FUNCTION RETURN VALUE "lerr" IS FALSE IN CASE SOMETHING WENT WRONG.
173
!=== ABOUT THE KEYS:
174
!     * The "keys" component (of type keys_type) is in principle enough to store everything we could need.
175
!     But some variables are stored as direct-access keys to make the code more readable and because they are used often.
176
!     * Most of the direct-access keys are set in this module, but some are not (longName, iadv, isAdvected for now).
177
!     * Some of the direct-access keys must be updated (using the routine "setDirectKeys") is a subset of "tracers(:)"
178
!     is extracted: the indexes are no longer valid for a subset (examples: iqParent, iqDescen).
179
!     * If you need to convert a %key/%val pair into a direct-access key, add the corresponding line in "setDirectKeys".
180
!==============================================================================================================================
181
1
LOGICAL FUNCTION readTracersFiles(type_trac, lRepr) RESULT(lerr)
182
!------------------------------------------------------------------------------------------------------------------------------
183
  CHARACTER(LEN=*),  INTENT(IN)  :: type_trac                        !--- List of components used
184
  LOGICAL, OPTIONAL, INTENT(IN)  :: lRepr                            !--- Activate the HNNO3 exceptions for REPROBUS
185
1
  CHARACTER(LEN=maxlen),  ALLOCATABLE :: s(:), sections(:), trac_files(:)
186
  CHARACTER(LEN=maxlen) :: str, fname, tname, pname, cname
187
  INTEGER               :: nsec, ierr, it, ntrac, ns, ip, ix, fType
188
  LOGICAL :: lRep
189
  TYPE(keys_type), POINTER :: k
190
!------------------------------------------------------------------------------------------------------------------------------
191
1
  lerr = .FALSE.
192
1
  modname = 'readTracersFiles'
193
1
  IF(.NOT.ALLOCATED(dBase)) ALLOCATE(dBase(0))
194
1
  lRep=.FALSE.; IF(PRESENT(lRepr)) lRep = lRepr
195
196
  !--- Required sections + corresponding files names (new style single section case) for tests
197

1
  IF(test(testTracersFiles(modname, type_trac, fType, .FALSE., trac_files, sections), lerr)) RETURN
198
1
  nsec = SIZE(sections)
199
200
  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
201
  SELECT CASE(fType)                         !--- Set %name, %genOName, %parent, %type, %phase, %component, %iGeneration, %keys
202
  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
203
    CASE(1)                                                          !=== OLD FORMAT "traceur.def"
204
    !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
205
      !--- OPEN THE "traceur.def" FILE
206
      OPEN(90, FILE="traceur.def", FORM='formatted', STATUS='old', IOSTAT=ierr)
207
208
      !--- GET THE TRACERS NUMBER
209
      READ(90,'(i3)',IOSTAT=ierr)ntrac                               !--- Number of lines/tracers
210
      IF(test(fmsg('Invalid format for "'//TRIM(fname)//'"', modname, ierr /= 0), lerr)) RETURN
211
212
      !--- READ THE REMAINING LINES: <hadv> <vadv> <tracer> [<transporting fluid>]
213
      IF(ALLOCATED(tracers)) DEALLOCATE(tracers)
214
      ALLOCATE(tracers(ntrac))
215
      DO it=1,ntrac                                                  !=== READ RAW DATA: loop on the line/tracer number
216
        READ(90,'(a)',IOSTAT=ierr) str
217
        IF(test(fmsg('Invalid format for "' //TRIM(fname)//'"', modname, ierr>0), lerr)) RETURN
218
        IF(test(fmsg('Not enough lines in "'//TRIM(fname)//'"', modname, ierr<0), lerr)) RETURN
219
        lerr = strParse(str, ' ', s, ns)
220
        CALL msg('This file is for air tracers only',           modname, ns == 3 .AND. it == 1)
221
        CALL msg('This files specifies the transporting fluid', modname, ns == 4 .AND. it == 1)
222
        k => tracers(it)%keys
223
224
        !=== NAME OF THE TRACER
225
        tname = old2newH2O(s(3), ip)
226
        ix = strIdx(oldHNO3, s(3))
227
        IF(ix /= 0 .AND. lRep) tname = newHNO3(ix)                   !--- Exception for HNO3 (REPROBUS ONLY)
228
        tracers(it)%name = tname                                     !--- Set %name
229
        CALL addKey_1('name', tname, k)                              !--- Set the name of the tracer
230
        tracers(it)%keys%name = tname                                !--- Copy tracers names in keys components
231
232
        !=== NAME OF THE COMPONENT
233
        cname = type_trac                                            !--- Name of the model component
234
        IF(ANY([(addPhase('H2O', ip), ip = 1, nphases)] == tname)) cname = 'lmdz'
235
        tracers(it)%component = cname                                !--- Set %component
236
        CALL addKey_1('component', cname, k)                         !--- Set the name of the model component
237
238
        !=== NAME OF THE PARENT
239
        pname = tran0                                                !--- Default name: default transporting fluid (air)
240
        IF(ns == 4) THEN
241
          pname = old2newH2O(s(4))
242
          ix = strIdx(oldHNO3, s(4))
243
          IF(ix /= 0 .AND. lRep) pname = newHNO3(ix)                 !--- Exception for HNO3 (REPROBUS ONLY)
244
        END IF
245
        tracers(it)%parent = pname                                   !--- Set %parent
246
        CALL addKey_1('parent', pname, k)
247
248
        !=== PHASE AND ADVECTION SCHEMES NUMBERS
249
        tracers(it)%phase = known_phases(ip:ip)                      !--- Set %phase:  tracer phase (default: "g"azeous)
250
        CALL addKey_1('phase', known_phases(ip:ip), k)               !--- Set the phase  of the tracer (default: "g"azeous)
251
        CALL addKey_1('hadv', s(1),  k)                              !--- Set the horizontal advection schemes number
252
        CALL addKey_1('vadv', s(2),  k)                              !--- Set the vertical   advection schemes number
253
      END DO
254
      CLOSE(90)
255
      IF(test(setGeneration(tracers), lerr)) RETURN                  !--- Set %iGeneration and %gen0Name
256
      WHERE(tracers%iGeneration == 2) tracers(:)%type = 'tag'        !--- Set %type:        'tracer' or 'tag'
257
      DO it=1,ntrac
258
        CALL addKey_1('type', tracers(it)%type, tracers(it)%keys)    !--- Set the type of tracer
259
      END DO
260
      IF(test(checkTracers(tracers, fname, fname), lerr)) RETURN     !--- Detect orphans and check phases
261
      IF(test(checkUnique (tracers, fname, fname), lerr)) RETURN     !--- Detect repeated tracers
262
      CALL sortTracers    (tracers)                                  !--- Sort the tracers
263
    !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
264


4
    CASE(2); IF(test(feedDBase(["tracer.def"], [type_trac], modname), lerr)) RETURN !=== SINGLE   FILE, MULTIPLE SECTIONS
265
    !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
266

1
    CASE(3); IF(test(feedDBase(  trac_files  ,  sections,   modname), lerr)) RETURN !=== MULTIPLE FILES, SINGLE  SECTION
267
  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
268
  END SELECT
269
  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
270
1
  IF(ALL([2,3] /= fType)) RETURN
271
272
1
  IF(nsec  == 1) THEN;
273











13
    tracers = dBase(1)%trac
274
  ELSE IF(tracs_merge) THEN
275
    CALL msg('The multiple required sections will be MERGED.',    modname)
276
    IF(test(mergeTracers(dBase, tracers), lerr)) RETURN
277
  ELSE
278
    CALL msg('The multiple required sections will be CUMULATED.', modname)
279
    IF(test(cumulTracers(dBase, tracers), lerr)) RETURN
280
  END IF
281
1
  CALL setDirectKeys(tracers)                                        !--- Set %iqParent, %iqDescen, %nqDescen, %nqChildren
282


2
END FUNCTION readTracersFiles
283
!==============================================================================================================================
284
285
286
!==============================================================================================================================
287
3
LOGICAL FUNCTION testTracersFiles(modname, type_trac, fType, lDisp, tracf, sects) RESULT(lerr)
288
  CHARACTER(LEN=*),                             INTENT(IN)  :: modname, type_trac
289
  INTEGER,                                      INTENT(OUT) :: fType
290
  LOGICAL,                            OPTIONAL, INTENT(IN)  :: lDisp
291
  CHARACTER(LEN=maxlen), ALLOCATABLE, OPTIONAL, INTENT(OUT) :: tracf(:), sects(:)
292
3
  CHARACTER(LEN=maxlen), ALLOCATABLE :: trac_files(:), sections(:)
293
  LOGICAL, ALLOCATABLE :: ll(:)
294
  LOGICAL :: lD, lFound
295
  INTEGER :: is, nsec
296
3
  lD = .FALSE.; IF(PRESENT(lDisp)) lD = lDisp
297
3
  lerr = .FALSE.
298
299
  !--- PARSE "type_trac" LIST AND DETERMINE THE TRACERS FILES NAMES (FOR CASE 3: MULTIPLE FILES, SINGLE SECTION PER FILE)
300
  !--- If type_trac is a scalar (case 1), "sections" and "trac_files" are not usable, but are meaningless for case 1 anyway.
301
3
  IF(test(strParse(type_trac, '|', sections,  n=nsec), lerr)) RETURN !--- Parse "type_trac" list
302




6
  IF(PRESENT(sects)) sects = sections
303


3
  ALLOCATE(trac_files(nsec), ll(nsec))
304
6
  DO is=1, nsec
305

3
     trac_files(is) = 'tracer_'//TRIM(sections(is))//'.def'
306
6
     INQUIRE(FILE=TRIM(trac_files(is)), EXIST=ll(is))
307
  END DO
308




6
  IF(PRESENT(tracf)) tracf = trac_files
309
3
  fType = 0
310
3
  INQUIRE(FILE='traceur.def', EXIST=lFound); IF(lFound)  fType = 1   !--- OLD STYLE FILE
311
3
  INQUIRE(FILE='tracer.def',  EXIST=lFound); IF(lFound)  fType = 2   !--- NEW STYLE ; SINGLE  FILE, SEVERAL SECTIONS
312

3
                                             IF(ALL(ll)) fType = 3   !--- NEW STYLE ; SEVERAL FILES, SINGLE SECTION USED
313
3
  IF(.NOT.lD) RETURN                                                 !--- NO CHECKING/DISPLAY NEEDED: JUST GET type_trac,fType
314


2
  IF(ANY(ll) .AND. fType/=3) THEN                                    !--- MISSING FILES
315
    IF(test(checkList(trac_files, .NOT.ll, 'Failed reading tracers description', 'files', 'missing'), lerr)) RETURN
316
  END IF
317
318
  !--- TELLS WHAT WAS IS ABOUT TO BE USED
319
1
  CALL msg('Trying to read old-style tracers description file "traceur.def"',                      modname, fType==1)
320
1
  CALL msg('Trying to read the new style multi-sections tracers description file "tracer.def"',    modname, fType==2)
321
1
  CALL msg('Trying to read the new style single section tracers description files "tracer_*.def"', modname, fType==3)
322

3
END FUNCTION testTracersFiles
323
!==============================================================================================================================
324
325
!==============================================================================================================================
326

1
LOGICAL FUNCTION feedDBase(fnames, snames, modname) RESULT(lerr)
327
! Purpose: Read the sections "snames(is)" (pipe-separated list) from each "fnames(is)"
328
!   file and create the corresponding tracers set descriptors in the database "dBase":
329
! * dBase(id)%name                : section name
330
! * dBase(id)%trac(:)%name        : tracers names
331
! * dBase(id)%trac(it)%keys%key(:): names  of keys associated to tracer dBase(id)%trac(it)%name
332
! * dBase(id)%trac(it)%keys%val(:): values of keys associated to tracer dBase(id)%trac(it)%name
333
!------------------------------------------------------------------------------------------------------------------------------
334
  CHARACTER(LEN=*), INTENT(IN)  :: fnames(:)                         !--- Files names
335
  CHARACTER(LEN=*), INTENT(IN)  :: snames(:)                         !--- Pipe-deparated list of sections (one list each file)
336
  CHARACTER(LEN=*), INTENT(IN)  :: modname                           !--- Calling routine name
337
1
  INTEGER,  ALLOCATABLE :: ndb(:)                                    !--- Number of sections for each file
338
  INTEGER,  ALLOCATABLE :: ixf(:)                                    !--- File index for each section of the expanded list
339
  CHARACTER(LEN=maxlen) :: fnm, snm
340
  INTEGER               :: idb, i
341
  LOGICAL :: ll
342
!------------------------------------------------------------------------------------------------------------------------------
343
  !=== READ THE REQUIRED SECTIONS
344
1
  ll = strCount(snames, '|', ndb)                                    !--- Number of sections for each file
345

2
  ALLOCATE(ixf(SUM(ndb)))
346
2
  DO i=1, SIZE(fnames)                                               !--- Set %name, %keys
347
1
    IF(test(readSections(fnames(i), snames(i), 'default'), lerr)) RETURN
348

4
    ixf(1+SUM(ndb(1:i-1)):SUM(ndb(1:i))) = i                         !--- File index for each section of the expanded list
349
  END DO
350
  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
351
2
  DO idb=1,SIZE(dBase)                                               !--- LOOP ON THE LOADED SECTIONS
352
  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
353
1
    fnm = fnames(ixf(idb)); snm = dBase(idb)%name                    !--- FILE AND SECTION NAMES
354


3
    lerr = ANY([(dispTraSection('RAW CONTENT OF SECTION "'//TRIM(snm)//'"', snm, modname), idb=1, SIZE(dBase))])
355
1
    IF(test(expandSection(dBase(idb)%trac, snm, fnm), lerr)) RETURN  !--- EXPAND NAMES ;  set %parent, %type, %component
356
1
    IF(test(setGeneration(dBase(idb)%trac),           lerr)) RETURN  !---                 set %iGeneration,   %genOName
357
1
    IF(test(checkTracers (dBase(idb)%trac, snm, fnm), lerr)) RETURN  !--- CHECK ORPHANS AND PHASES
358
1
    IF(test(checkUnique  (dBase(idb)%trac, snm, fnm), lerr)) RETURN  !--- CHECK TRACERS UNIQUENESS
359
1
    CALL expandPhases    (dBase(idb)%trac)                           !--- EXPAND PHASES ; set %phase
360
1
    CALL sortTracers     (dBase(idb)%trac)                           !--- SORT TRACERS
361


4
    lerr = ANY([(dispTraSection('EXPANDED CONTENT OF SECTION "'//TRIM(snm)//'"', snm, modname), idb=1, SIZE(dBase))])
362
  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
363
  END DO
364
  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
365

1
END FUNCTION feedDBase
366
!------------------------------------------------------------------------------------------------------------------------------
367
368
!------------------------------------------------------------------------------------------------------------------------------
369
1
LOGICAL FUNCTION readSections(fnam,snam,defName) RESULT(lerr)
370
!------------------------------------------------------------------------------------------------------------------------------
371
  CHARACTER(LEN=*),           INTENT(IN) :: fnam                     !--- File name
372
  CHARACTER(LEN=*),           INTENT(IN) :: snam                     !--- Pipe-separated sections list
373
  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: defName                  !--- Special section (default values) name
374
!------------------------------------------------------------------------------------------------------------------------------
375
1
  TYPE(dataBase_type),   ALLOCATABLE :: tdb(:)
376
1
  CHARACTER(LEN=maxlen), ALLOCATABLE :: sec(:)
377
1
  INTEGER,               ALLOCATABLE ::  ix(:)
378
  INTEGER :: n0, idb, ndb
379
  LOGICAL :: ll
380
!------------------------------------------------------------------------------------------------------------------------------
381
1
  n0 = SIZE(dBase) + 1                                               !--- Index for next entry in the database
382
1
  CALL readSections_all()                                            !--- Read all the sections of file "fnam"
383
1
  ndb= SIZE(dBase)                                                   !--- Current number of sections in the database
384
1
  IF(PRESENT(defName)) THEN                                          !--- Add default values to all the tracers
385
2
    DO idb=n0,ndb; CALL addDefault(dBase(idb)%trac, defName); END DO !--- and remove the virtual tracer "defName"
386
  END IF
387
1
  ll = strParse(snam, '|', keys = sec)                               !--- Requested sections names
388

3
  ix = strIdx(dBase(:)%name, sec(:))                                 !--- Indexes of requested sections in database
389

2
  IF(test(checkList(sec, ix == 0, 'In file "'//TRIM(fnam)//'"','section(s)', 'missing'), lerr)) RETURN
390




































29
  tdb = dBase(:); dBase = [tdb(1:n0-1),tdb(PACK(ix, MASK=ix/=0))]    !--- Keep requested sections only
391
392
CONTAINS
393
394
!------------------------------------------------------------------------------------------------------------------------------
395
1
SUBROUTINE readSections_all()
396
!------------------------------------------------------------------------------------------------------------------------------
397
1
  CHARACTER(LEN=maxlen), ALLOCATABLE ::  s(:), v(:)
398
  TYPE(trac_type),       ALLOCATABLE :: tt(:)
399
1
  TYPE(trac_type)       :: tmp
400
  CHARACTER(LEN=1024)   :: str, str2
401
  CHARACTER(LEN=maxlen) :: secn
402
  INTEGER               :: ierr, n
403
!------------------------------------------------------------------------------------------------------------------------------
404

1
  IF(.NOT.ALLOCATED(dBase)) ALLOCATE(dBase(0))
405
1
  OPEN(90, FILE=fnam, FORM='formatted', STATUS='old')
406
9
  DO; str=''
407
    DO
408
9
      READ(90,'(a)', IOSTAT=ierr)str2                                !--- Read a full line
409
9
      str=TRIM(str)//' '//TRIM(str2)                                 !--- Append "str" with the current line
410
9
      n=LEN_TRIM(str); IF(n == 0) EXIT                               !--- Empty line (probably end of file)
411
9
      IF(IACHAR(str(n:n))  /= 92) EXIT                               !--- No "\" continuing line symbol found => end of line
412
9
      str = str(1:n-1)                                               !--- Remove the "\" continuing line symbol
413
    END DO
414
9
    str = ADJUSTL(str)                                               !--- Remove the front space
415
9
    IF(ierr    /= 0 ) EXIT                                           !--- Finished: error or end of file
416
8
    IF(str(1:1)=='#') CYCLE                                          !--- Skip comments lines
417
8
    CALL removeComment(str)                                          !--- Skip comments at the end of a line
418
8
    IF(str     == '') CYCLE                                          !--- Skip empty line (probably at the end of the file)
419
8
    IF(str(1:1)=='&') THEN                                           !=== SECTION HEADER LINE
420
2
      ndb  = SIZE(dBase)                                             !--- Number of sections so far
421
2
      secn = str(2:LEN_TRIM(str))//' '                               !--- Current section name
422

2
      IF(ANY(dBase(:)%name == secn)) CYCLE                           !--- Already known section
423
2
      IF(secn(1:7) == 'version')     CYCLE                           !--- Skip the "version" special section
424
1
      ndb = ndb + 1                                                  !--- Extend database
425





2
      ALLOCATE(tdb(ndb))
426






1
      tdb(1:ndb-1)  = dBase
427
1
      tdb(ndb)%name = secn
428



1
      ALLOCATE(tdb(ndb)%trac(0))
429




1
      CALL MOVE_ALLOC(FROM=tdb, TO=dBase)
430
    ELSE                                                             !=== TRACER LINE
431

6
      ll = strParse(str,' ', s, n, v)                                !--- Parse <key>=<val> pairs
432





42
      tt = dBase(ndb)%trac(:)
433


6
      tmp%name = s(1); tmp%keys = keys_type(s(1), s(2:n), v(2:n))    !--- Set %name and %keys
434












84
      dBase(ndb)%trac = [tt(:), tmp]
435



21
      DEALLOCATE(tt)
436
!      dBase(ndb)%trac = [dBase(ndb)%trac(:), tra(name=s(1), keys=keys_type(s(1), s(2:n), v(2:n)))]
437
    END IF
438
  END DO
439
1
  CLOSE(90)
440
441


1
END SUBROUTINE readSections_all
442
!------------------------------------------------------------------------------------------------------------------------------
443
444
END FUNCTION readSections
445
!==============================================================================================================================
446
447
448
!==============================================================================================================================
449
1
SUBROUTINE addDefault(t, defName)
450
!------------------------------------------------------------------------------------------------------------------------------
451
! Purpose: Add the keys from virtual tracer named "defName" (if any) and remove this virtual tracer.
452
!------------------------------------------------------------------------------------------------------------------------------
453
  TYPE(trac_type), ALLOCATABLE, TARGET, INTENT(INOUT) :: t(:)
454
  CHARACTER(LEN=*),                     INTENT(IN)    :: defName
455
  INTEGER :: jd, it, k
456
  TYPE(keys_type), POINTER :: ky
457
  TYPE(trac_type), ALLOCATABLE :: tt(:)
458

7
  jd = strIdx(t(:)%name, defName)
459
1
  IF(jd == 0) RETURN
460
1
  ky => t(jd)%keys
461
6
  DO k = 1, SIZE(ky%key)                                             !--- Loop on the keys of the tracer named "defName"
462
!   CALL addKey_m(ky%key(k), ky%val(k), t(:)%keys, .FALSE.)           !--- Add key to all the tracers (no overwriting)
463
36
    DO it = 1, SIZE(t); CALL addKey_1(ky%key(k), ky%val(k), t(it)%keys, .FALSE.); END DO
464
  END DO
465










23
  tt = [t(1:jd-1),t(jd+1:SIZE(t))]; CALL MOVE_ALLOC(FROM=tt, TO=t)   !--- Remove the virtual tracer named "defName"
466
END SUBROUTINE addDefault
467
!==============================================================================================================================
468
469
!==============================================================================================================================
470
SUBROUTINE subDefault(t, defName, lSubLocal)
471
!------------------------------------------------------------------------------------------------------------------------------
472
! Purpose: Substitute the keys from virtual tracer named "defName" (if any) and remove this virtual tracer.
473
!          Substitute the keys locally (for the current tracer) if the flag "lSubLocal" is .TRUE.
474
!------------------------------------------------------------------------------------------------------------------------------
475
  TYPE(trac_type), ALLOCATABLE, TARGET, INTENT(INOUT) :: t(:)
476
  CHARACTER(LEN=*),                     INTENT(IN)    :: defName
477
  LOGICAL,                              INTENT(IN)    :: lSubLocal
478
  INTEGER :: i0, it, ik
479
  TYPE(keys_type), POINTER     :: k0, ky
480
  TYPE(trac_type), ALLOCATABLE :: tt(:)
481
  i0 = strIdx(t(:)%name, defName)
482
  IF(i0 == 0) RETURN
483
  k0 => t(i0)%keys
484
  DO it = 1, SIZE(t); IF(it == i0) CYCLE                             !--- Loop on the tracers
485
    ky => t(it)%keys
486
487
    !--- Substitute in the values of <key>=<val> pairs the keys defined in the virtual tracer "defName"
488
    DO ik = 1, SIZE(k0%key); CALL strReplace(ky%val, k0%key(ik), k0%val(ik), .TRUE.); END DO
489
490
    IF(.NOT.lSubLocal) CYCLE
491
    !--- Substitute in the values of <key>=<val> pairs the keys defined locally (in the current tracer)
492
    DO ik = 1, SIZE(ky%key); CALL strReplace(ky%val, ky%key(ik), ky%val(ik), .TRUE.); END DO
493
  END DO
494
  tt = [t(1:i0-1),t(i0+1:SIZE(t))]; CALL MOVE_ALLOC(FROM=tt, TO=t)   !--- Remove the virtual tracer named "defName"
495
496
END SUBROUTINE subDefault
497
!==============================================================================================================================
498
499
500
!==============================================================================================================================
501
1
LOGICAL FUNCTION expandSection(tr, sname, fname) RESULT(lerr)
502
!------------------------------------------------------------------------------------------------------------------------------
503
! Purpose: Expand tracers and parents lists in the tracers descriptor "tra".
504
! Note:  * The following keys are expanded, so are accessible explicitely using "%" operator: "parent" "type".
505
!        * Default values are provided for these keys because they are necessary.
506
!------------------------------------------------------------------------------------------------------------------------------
507
  TYPE(trac_type), ALLOCATABLE, INTENT(INOUT) :: tr(:)                 !--- Tracer derived type vector
508
  CHARACTER(LEN=*),             INTENT(IN)    :: sname
509
  CHARACTER(LEN=*), OPTIONAL,   INTENT(IN)    :: fname
510
  TYPE(trac_type),       ALLOCATABLE :: ttr(:)
511
1
  CHARACTER(LEN=maxlen), ALLOCATABLE :: ta(:), pa(:)
512
  CHARACTER(LEN=maxlen) :: msg1, modname
513
  INTEGER :: it, nt, iq, nq, itr, ntr, ipr, npr
514
  LOGICAL :: ll
515
1
  modname = 'expandSection'
516
1
  lerr = .FALSE.
517
1
  nt = SIZE(tr)
518
  nq = 0
519
  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
520
6
  DO it = 1, nt    !=== GET TRACERS NB AFTER EXPANSION + NEEDED KEYS (name, parent, type)
521
  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
522
    !--- Extract useful keys: parent name, type, component name
523

30
    tr(it)%parent    = fgetKey(it, 'parent', tr(:)%keys,  tran0  )
524

30
    tr(it)%type      = fgetKey(it, 'type'  , tr(:)%keys, 'tracer')
525
5
    tr(it)%component = sname
526
!   CALL addKey_m('component', sname, tr(:)%keys)
527
30
    DO iq=1,SIZE(tr); CALL addKey_1('component', sname, tr(iq)%keys); END DO
528
529
    !--- Determine the number of tracers and parents ; coherence checking
530
5
    ll = strCount(tr(it)%name,   ',', ntr)
531
5
    ll = strCount(tr(it)%parent, ',', npr)
532
533
    !--- Tagging tracers only can have multiple parents
534

10
    IF(test(npr/=1 .AND. TRIM(tr(it)%type)/='tag', lerr)) THEN
535
      msg1 = 'Check section "'//TRIM(sname)//'"'
536
      IF(PRESENT(fname)) msg1=TRIM(msg1)//' in file "'//TRIM(fname)//'"'
537
      CALL msg(TRIM(msg1)//': "'//TRIM(tr(it)%name)//'" has several parents but is not a tag', modname); RETURN
538
    END IF
539
6
    nq = nq + ntr*npr
540
  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
541
  END DO
542
  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
543
544



6
  ALLOCATE(ttr(nq))
545
  iq = 1
546
  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
547
6
  DO it = 1, nt                                                      !=== EXPAND TRACERS AND PARENTS NAMES LISTS
548
  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
549
5
    ll = strParse(tr(it)%name,   ',', ta, ntr)                       !--- Number of tracers
550
5
    ll = strParse(tr(it)%parent, ',', pa, npr)                       !--- Number of parents
551
11
    DO ipr=1,npr                                                     !--- Loop on parents list elts
552
15
      DO itr=1,ntr                                                   !--- Loop on tracers list elts
553




50
        ttr(iq)%keys%key  = tr(it)%keys%key
554




50
        ttr(iq)%keys%val  = tr(it)%keys%val
555
5
        ttr(iq)%keys%name = ta(itr)
556
5
        ttr(iq)%name      = TRIM(ta(itr));    CALL addKey_1('name',      ta(itr),          ttr(iq)%keys)
557
5
        ttr(iq)%parent    = TRIM(pa(ipr));    CALL addKey_1('parent',    pa(ipr),          ttr(iq)%keys)
558
5
        ttr(iq)%type      = tr(it)%type;      CALL addKey_1('type',      tr(it)%type,      ttr(iq)%keys)
559
5
        ttr(iq)%component = tr(it)%component; CALL addKey_1('component', tr(it)%component, ttr(iq)%keys)
560
10
        iq = iq+1
561
      END DO
562
    END DO
563
  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
564
  END DO
565
  !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
566

1
  DEALLOCATE(ta,pa)
567



6
  CALL MOVE_ALLOC(FROM=ttr, TO=tr)
568
569

2
END FUNCTION expandSection
570
!==============================================================================================================================
571
572
573
!==============================================================================================================================
574
1
LOGICAL FUNCTION setGeneration(tr) RESULT(lerr)
575
!------------------------------------------------------------------------------------------------------------------------------
576
! Purpose: Determine, for each tracer of "tr(:)":
577
!   * %iGeneration: the generation number
578
!   * %gen0Name:    the generation 0 ancestor name
579
!          Check also for orphan tracers (tracers not descending on "tran0").
580
!------------------------------------------------------------------------------------------------------------------------------
581
  TYPE(trac_type),     INTENT(INOUT) :: tr(:)                        !--- Tracer derived type vector
582
  INTEGER                            :: iq, jq, ig
583
1
  CHARACTER(LEN=maxlen), ALLOCATABLE :: parent(:)
584
!------------------------------------------------------------------------------------------------------------------------------
585
  CHARACTER(LEN=maxlen) :: modname
586
1
  modname = 'setGeneration'
587

6
  IF(test(fmsg('missing "parent" attribute', modname, getKey('parent', parent, ky=tr(:)%keys)), lerr)) RETURN
588
6
  DO iq = 1, SIZE(tr)
589
5
    jq = iq; ig = 0
590
5
    DO WHILE(parent(jq) /= tran0)
591
      jq = strIdx(tr(:)%name, parent(jq))
592
      IF(test(fmsg('Orphan tracer "'//TRIM(tr(iq)%name)//'"', modname, jq == 0), lerr)) RETURN
593
      ig = ig + 1
594
    END DO
595
5
    tr(iq)%gen0Name = tr(jq)%name; CALL addKey_1('gen0Name',    tr(iq)%gen0Name,   tr(iq)%keys)
596
6
    tr(iq)%iGeneration = ig;       CALL addKey_1('iGeneration', TRIM(int2str(ig)), tr(iq)%keys)
597
  END DO
598
1
END FUNCTION setGeneration
599
!==============================================================================================================================
600
601
602
!==============================================================================================================================
603
1
LOGICAL FUNCTION checkTracers(tr, sname, fname) RESULT(lerr)
604
!------------------------------------------------------------------------------------------------------------------------------
605
! Purpose:
606
!   * check for orphan tracers (without known parent)
607
!   * check wether the phases are known or not ("g"aseous, "l"iquid or "s"olid so far)
608
!------------------------------------------------------------------------------------------------------------------------------
609
  TYPE(trac_type),            INTENT(IN) :: tr(:)                    !--- Tracer derived type vector
610
  CHARACTER(LEN=*),           INTENT(IN) :: sname                    !--- Section name
611
  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: fname                    !--- File name
612
  CHARACTER(LEN=maxlen) :: mesg
613
2
  CHARACTER(LEN=maxlen) :: bp(SIZE(tr, DIM=1)), pha                  !--- Bad phases list, phases of current tracer
614
  CHARACTER(LEN=1) :: p
615
  INTEGER :: ip, np, iq, nq
616
!------------------------------------------------------------------------------------------------------------------------------
617
  nq = SIZE(tr,DIM=1)                                                !--- Number of tracers lines
618
1
  mesg = 'Check section "'//TRIM(sname)//'"'
619

1
  IF(PRESENT(fname)) mesg=TRIM(mesg)//' in file "'//TRIM(fname)//'"'
620
621
  !=== CHECK FOR ORPHAN TRACERS
622


11
  IF(test(checkList(tr%name, tr%iGeneration==-1, mesg, 'tracers', 'orphan'), lerr)) RETURN
623
624
  !=== CHECK PHASES
625

6
  DO iq=1,nq; IF(tr(iq)%iGeneration/=0) CYCLE                        !--- Generation O only is checked
626

30
    pha = fgetKey(iq, 'phases', tr(:)%keys, 'g')                     !--- Phases
627
5
    np = LEN_TRIM(pha); bp(iq)=' '
628


10
    DO ip=1,np; p = pha(ip:ip); IF(INDEX(known_phases,p)==0) bp(iq) = TRIM(bp(iq))//p; END DO
629

6
    IF(TRIM(bp(iq)) /= '') bp(iq) = TRIM(tr(iq)%name)//': '//TRIM(bp(iq))
630
  END DO
631

6
  lerr = checkList(bp, tr%iGeneration==0 .AND. bp/='', mesg, 'tracers phases', 'unknown')
632
1
END FUNCTION checkTracers
633
!==============================================================================================================================
634
635
636
!==============================================================================================================================
637
1
LOGICAL FUNCTION checkUnique(tr, sname, fname) RESULT(lerr)
638
!------------------------------------------------------------------------------------------------------------------------------
639
! Purpose: Make sure that tracers are not repeated.
640
!------------------------------------------------------------------------------------------------------------------------------
641
  TYPE(trac_type),            INTENT(IN) :: tr(:)                    !--- Tracer derived type vector
642
  CHARACTER(LEN=*),           INTENT(IN) :: sname                    !--- Section name
643
  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: fname                    !--- File name
644
!------------------------------------------------------------------------------------------------------------------------------
645
  INTEGER :: ip, np, iq, nq, k
646
1
  LOGICAL, ALLOCATABLE  :: ll(:)
647
2
  CHARACTER(LEN=maxlen) :: mesg, tnam, tdup(SIZE(tr,DIM=1))
648
  CHARACTER(LEN=1)      :: p
649
!------------------------------------------------------------------------------------------------------------------------------
650
1
  mesg = 'Check section "'//TRIM(sname)//'"'
651

1
  IF(PRESENT(fname)) mesg=TRIM(mesg)//' in file "'//TRIM(fname)//'"'
652
  nq=SIZE(tr,DIM=1); lerr=.FALSE.                                    !--- Number of lines ; error flag
653
6
  tdup(:) = ''
654

6
  DO iq=1,nq; IF(tr(iq)%type == 'tag') CYCLE                         !--- Tags can be repeated
655
5
    tnam = TRIM(tr(iq)%name)
656



35
    ll = tr(:)%name==TRIM(tnam)                                      !--- Mask for current tracer name
657

30
    IF(COUNT(ll)==1 ) CYCLE                                          !--- Tracer is not repeated
658
3
    IF(tr(iq)%iGeneration>0) THEN
659
      tdup(iq) = tnam                                                !--- gen>0: MUST be unique
660
    ELSE
661
18
      DO ip=1,nphases; p=known_phases(ip:ip)                         !--- Loop on known phases
662
        !--- Number of appearances of the current tracer with known phase "p"
663




660
        np = COUNT( PACK( [(INDEX(fgetKey(k, 'phases', tr(:)%keys, 'g'),p), k=1, nq)] /=0 , MASK=ll ) )
664
15
        IF(np <=1) CYCLE
665
        tdup(iq) = TRIM(tdup(iq))//TRIM(phases_names(ip))
666

3
        IF(ANY(tdup(1:iq-1) == tdup(iq))) tdup(iq)=''                !--- Avoid repeating same messages
667
      END DO
668
    END IF
669

4
    IF(tdup(iq) /= '') tdup(iq)=TRIM(tnam)//' in '//TRIM(tdup(iq))//' phase(s)'
670
  END DO
671

6
  lerr = checkList(tdup, tdup/='', mesg, 'tracers', 'duplicated')
672
1
END FUNCTION checkUnique
673
!==============================================================================================================================
674
675
676
!==============================================================================================================================
677
1
SUBROUTINE expandPhases(tr)
678
!------------------------------------------------------------------------------------------------------------------------------
679
! Purpose: Expand the phases in the tracers descriptor "tr". Phases are not repeated for a tracer, thanks to "checkUnique".
680
!------------------------------------------------------------------------------------------------------------------------------
681
  TYPE(trac_type), ALLOCATABLE, INTENT(INOUT) :: tr(:)               !--- Tracer derived type vector
682
!------------------------------------------------------------------------------------------------------------------------------
683
  TYPE(trac_type), ALLOCATABLE :: ttr(:)
684
  INTEGER,   ALLOCATABLE ::  i0(:)
685
  CHARACTER(LEN=maxlen)  :: nam, pha, tname
686
  CHARACTER(LEN=1) :: p
687
  INTEGER :: ip, np, iq, jq, nq, it, nt, nc, i, n
688
  LOGICAL :: lTag, lExt
689
!------------------------------------------------------------------------------------------------------------------------------
690
1
  nq = SIZE(tr, DIM=1)
691
  nt = 0
692
6
  DO iq = 1, nq                                                      !--- GET THE NUMBER OF TRACERS
693
5
    IF(tr(iq)%iGeneration /= 0) CYCLE                                !--- Only deal with generation 0 tracers
694

30
    nc = COUNT(tr(:)%gen0Name==tr(iq)%name .AND. tr%iGeneration/=0)  !--- Number of children of tr(iq)
695

30
    tr(iq)%phase = fgetKey(iq, 'phases', tr(:)%keys)                 !--- Phases list        of tr(iq)
696
5
    np = LEN_TRIM(tr(iq)%phase)                                      !--- Number of phases   of tr(iq)
697
6
    nt = nt + (1+nc) * np                                            !--- Number of tracers after expansion
698
  END DO
699



6
  ALLOCATE(ttr(nt))                                                  !--- Version  of "tr" after phases expansion
700
  it = 1                                                             !--- Current "ttr(:)" index
701
6
  DO iq = 1, nq                                                      !--- Loop on "tr(:)" indexes
702
5
    lTag = tr(iq)%type=='tag'                                        !--- Current tracer is a tag
703




46
    i0 = strFind(tr(:)%name, TRIM(tr(iq)%gen0Name), n)               !--- Indexes of first generation ancestor copies
704

27
    np = SUM([( LEN_TRIM(tr(i0(i))%phase),i=1,n )], 1)               !--- Number of phases for current tracer tr(iq)
705
5
    lExt = np>1                                                      !--- Phase suffix only required if phases number is > 1
706

5
    IF(lTag) lExt = lExt .AND. tr(iq)%iGeneration>0                  !--- No phase suffix for generation 0 tags
707
6
    DO i=1,n                                                         !=== LOOP ON GENERATION 0 ANCESTORS
708
5
      jq = i0(i)                                                     !--- tr(jq): ith tracer with same gen 0 ancestor as tr(iq)
709
5
      IF(tr(iq)%iGeneration==0) jq=iq                                !--- Generation 0: count the current tracer phases only
710
5
      pha = tr(jq)%phase                                             !--- Phases list for tr(jq)
711
10
      DO ip = 1, LEN_TRIM(pha)                                       !=== LOOP ON PHASES LISTS
712
5
        p = pha(ip:ip)
713
5
        tname = TRIM(tr(iq)%name); nam = tname                       !--- Tracer name (regular case)
714
5
        IF(lTag) nam = TRIM(tr(iq)%parent)                           !--- Parent name (tagging case)
715
5
        IF(lExt) nam = addPhase(nam, p )                             !--- Phase extension needed
716
5
        IF(lTag) nam = TRIM(nam)//'_'//TRIM(tname)                   !--- <parent>_<name> for tags
717




5
        ttr(it) = tr(iq)                                             !--- Same <key>=<val> pairs
718
5
        ttr(it)%name      = TRIM(nam)                                !--- Name with possibly phase suffix
719
5
        ttr(it)%keys%name = TRIM(nam)                                !--- Name inside the keys decriptor
720
5
        ttr(it)%phase     = p                                        !--- Single phase entry
721
5
        CALL addKey_1('name', nam, ttr(it)%keys)
722
5
        CALL addKey_1('phase', p,  ttr(it)%keys)
723

5
        IF(lExt .AND. tr(iq)%iGeneration>0) THEN
724
          ttr(it)%parent   = addPhase(tr(iq)%parent,   p)
725
          ttr(it)%gen0Name = addPhase(tr(iq)%gen0Name, p)
726
          CALL addKey_1('parent',   ttr(it)%parent,   ttr(it)%keys)
727
          CALL addKey_1('gen0Name', ttr(it)%gen0Name, ttr(it)%keys)
728
        END IF
729
10
        it = it+1
730
      END DO
731
5
      IF(tr(iq)%iGeneration==0) EXIT                                 !--- Break phase loop for gen 0
732
    END DO
733
  END DO
734



6
  CALL MOVE_ALLOC(FROM=ttr, TO=tr)
735
2
  CALL delKey(['phases'],tr)                                         !--- Remove few keys entries
736
737
1
END SUBROUTINE expandPhases
738
!==============================================================================================================================
739
740
741
!==============================================================================================================================
742
1
SUBROUTINE sortTracers(tr)
743
!------------------------------------------------------------------------------------------------------------------------------
744
! Purpose: Sort tracers:
745
!  * Put water at the beginning of the vector, in the "known_phases" order.
746
!  * lGrowGen == T: in ascending generations numbers.
747
!  * lGrowGen == F: tracer + its children sorted by growing generation, one after the other.
748
!   TO BE ADDED IF NECESSARY: HIGHER MOMENTS AT THE END
749
!------------------------------------------------------------------------------------------------------------------------------
750
  TYPE(trac_type), ALLOCATABLE, INTENT(INOUT) :: tr(:)         !--- Tracer derived type vector
751
!------------------------------------------------------------------------------------------------------------------------------
752
  TYPE(trac_type), ALLOCATABLE        :: tr2(:)
753
  INTEGER,         ALLOCATABLE        :: iy(:), iz(:)
754
2
  INTEGER                             :: ig, ng, iq, jq, ip, nq, n, ix(SIZE(tr)), k
755
!  tr2 is introduced in order to cope with a bug in gfortran 4.8.5 compiler
756
!------------------------------------------------------------------------------------------------------------------------------
757
  nq = SIZE(tr)
758
6
  DO ip = nphases, 1, -1
759

30
    iq = strIdx(tr(:)%name, addPhase('H2O', ip))
760
5
    IF(iq == 0) CYCLE
761











26
    tr2 = tr(:)
762













36
    tr = [tr2(iq), tr2(1:iq-1), tr2(iq+1:nq)]
763
  END DO
764
1
  IF(lSortByGen) THEN
765
    iq = 1
766
6
    ng = MAXVAL(tr(:)%iGeneration, MASK=.TRUE., DIM=1)               !--- Number of generations
767
2
    DO ig = 0, ng                                                    !--- Loop on generations
768



16
      iy = PACK([(k, k=1, nq)], MASK=tr(:)%iGeneration==ig)          !--- Generation ig tracers indexes
769
1
      n = SIZE(iy)
770
6
      ix(iq:iq+n-1) = iy                                             !--- Stack growing generations idxs
771
1
      iq = iq + n
772
    END DO
773
  ELSE
774
    iq = 1
775
    DO jq = 1, nq                                                    !--- Loop on generation 0 tracers
776
      IF(tr(jq)%iGeneration /= 0) CYCLE                              !--- Skip generations /= 0
777
      ix(iq) = jq                                                    !--- Generation 0 ancestor index first
778
      iq = iq + 1                                                    !--- Next "iq" for next generations tracers
779
      iy = strFind(tr(:)%gen0Name, TRIM(tr(jq)%name))                !--- Indexes of "tr(jq)" children in "tr(:)"
780
      ng = MAXVAL(tr(iy)%iGeneration, MASK=.TRUE., DIM=1)            !--- Number of generations of the "tr(jq)" family
781
      DO ig = 1, ng                                                  !--- Loop   on generations of the "tr(jq)" family
782
        iz = find(tr(iy)%iGeneration, ig, n)                         !--- Indexes of the tracers "tr(iy(:))" of generation "ig"
783
        ix(iq:iq+n-1) = iy(iz)                                       !--- Same indexes in "tr(:)"
784
        iq = iq + n
785
      END DO
786
    END DO
787
  END IF
788











12
  tr = tr(ix)                                                        !--- Reorder the tracers
789




6
END SUBROUTINE sortTracers
790
!==============================================================================================================================
791
792
793
!==============================================================================================================================
794
LOGICAL FUNCTION mergeTracers(sections, tr) RESULT(lerr)
795
  TYPE(dataBase_type),  TARGET, INTENT(IN)  :: sections(:)
796
  TYPE(trac_type), ALLOCATABLE, INTENT(OUT) ::       tr(:)
797
  TYPE(trac_type), POINTER ::   t1(:),   t2(:)
798
  INTEGER,     ALLOCATABLE :: ixct(:), ixck(:)
799
  INTEGER :: is, k1, k2, nk2, i1, i2, nt2
800
  CHARACTER(LEN=maxlen) :: s1, v1, v2, tnam, knam, modname
801
  modname = 'mergeTracers'
802
  lerr = .FALSE.
803
  t1 => sections(1)%trac(:)                                          !--- Alias: first tracers section
804
  tr = t1
805
  !----------------------------------------------------------------------------------------------------------------------------
806
  DO is=2,SIZE(sections)                                             !=== SEVERAL SECTIONS: MERGE THEM
807
  !----------------------------------------------------------------------------------------------------------------------------
808
    t2  => sections(is)%trac(:)                                      !--- Alias: current tracers section
809
    nt2  = SIZE(t2(:), DIM=1)                                        !--- Number of tracers in section
810
    ixct = strIdx(t1(:)%name, t2(:)%name)                            !--- Indexes of common tracers
811
    tr = [tr, PACK(t2, MASK= ixct==0)]                               !--- Append with new tracers
812
    IF( ALL(ixct == 0) ) CYCLE                                       !--- No common tracers => done
813
    CALL msg('Tracers defined in previous sections and duplicated in "'//TRIM(sections(is)%name)//'":', modname)
814
    CALL msg(t1(PACK(ixct, MASK = ixct/=0))%name, modname, nmax=128) !--- Display duplicates (the 128 first at most)
815
    !--------------------------------------------------------------------------------------------------------------------------
816
    DO i2=1,nt2; tnam = TRIM(t2(i2)%name)                            !=== LOOP ON COMMON TRACERS
817
    !--------------------------------------------------------------------------------------------------------------------------
818
      i1 = ixct(i2); IF(i1 == 0) CYCLE                               !--- Idx in t1(:) ; skip new tracers
819
820
      !=== CHECK WETHER ESSENTIAL KEYS ARE IDENTICAL OR NOT
821
      s1=' of "'//TRIM(tnam)//'" in "'//TRIM(sections(is)%name)//'" not matching previous value'
822
823
      IF(test(fmsg('Parent name'//TRIM(s1), modname, t1(i1)%parent      /= t2(i2)%parent),      lerr)) RETURN
824
      IF(test(fmsg('Type'       //TRIM(s1), modname, t1(i1)%type        /= t2(i2)%type),        lerr)) RETURN
825
      IF(test(fmsg('Generation' //TRIM(s1), modname, t1(i1)%iGeneration /= t2(i2)%iGeneration), lerr)) RETURN
826
827
      !=== APPEND <key>=<val> PAIRS NOT PREVIOULSLY DEFINED
828
      nk2  = SIZE(t2(i2)%keys%key(:))                                !--- Keys number in current section
829
      ixck = strIdx(t1(i1)%keys%key(:), t2(i2)%keys%key(:))          !--- Common keys indexes
830
831
      !=== APPEND NEW KEYS
832
      tr(i1)%keys%key = [ tr(i1)%keys%key, PACK(tr(i2)%keys%key, MASK = ixck==0)]
833
      tr(i1)%keys%val = [ tr(i1)%keys%val, PACK(tr(i2)%keys%val, MASK = ixck==0)]
834
835
      !--- KEEP TRACK OF THE COMPONENTS NAMES
836
      tr(i1)%component = TRIM(tr(i1)%component)//','//TRIM(tr(i2)%component)
837
838
      !--- SELECT COMMON TRACERS WITH DIFFERING KEYS VALUES (PREVIOUS VALUE IS KEPT)
839
      DO k2=1,nk2
840
        k1 = ixck(k2); IF(k1 == 0) CYCLE
841
        IF(t1(i1)%keys%val(k1) == t2(i2)%keys%val(k2)) ixck(k2)=0
842
      END DO
843
      IF(ALL(ixck==0)) CYCLE                                         !--- No identical keys with /=values
844
845
      !--- DISPLAY INFORMATION: OLD VALUES ARE KEPT FOR THE KEYS FOUND IN PREVIOUS AND CURRENT SECTIONS
846
      CALL msg('Key(s)'//TRIM(s1), modname)
847
      DO k2 = 1, nk2                                                 !--- Loop on keys found in both t1(:) and t2(:)
848
        knam = t2(i2)%keys%key(k2)                                   !--- Name of the current key
849
        k1 = ixck(k2)                                                !--- Corresponding index in t1(:)
850
        IF(k1 == 0) CYCLE                                            !--- New keys are skipped
851
        v1 = t1(i1)%keys%val(k1); v2 = t2(i2)%keys%val(k2)           !--- Key values in t1(:) and t2(:)
852
        CALL msg(' * '//TRIM(knam)//'='//TRIM(v2)//' ; previous value kept:'//TRIM(v1), modname)
853
      END DO
854
      !------------------------------------------------------------------------------------------------------------------------
855
    END DO
856
    !--------------------------------------------------------------------------------------------------------------------------
857
  END DO
858
  CALL sortTracers(tr)
859
860
END FUNCTION mergeTracers
861
!==============================================================================================================================
862
863
!==============================================================================================================================
864
LOGICAL FUNCTION cumulTracers(sections, tr) RESULT(lerr)
865
  TYPE(dataBase_type),  TARGET, INTENT(IN)  :: sections(:)
866
  TYPE(trac_type), ALLOCATABLE, INTENT(OUT) ::       tr(:)
867
  TYPE(trac_type), POINTER     :: t(:)
868
  INTEGER,   ALLOCATABLE :: nt(:)
869
  CHARACTER(LEN=maxlen)  :: tnam, tnam_new
870
  INTEGER :: iq, nq, is, ns, nsec
871
  lerr = .FALSE.                                                     !--- Can't fail ; kept to match "mergeTracer" interface.
872
  nsec =  SIZE(sections)
873
  tr = [(      sections(is)%trac(:) , is=1, nsec )]                  !--- Concatenated tracers vector
874
  nt = [( SIZE(sections(is)%trac(:)), is=1, nsec )]                  !--- Number of tracers in each section
875
  !----------------------------------------------------------------------------------------------------------------------------
876
  DO is=1, nsec                                                      !=== LOOP ON SECTIONS
877
  !----------------------------------------------------------------------------------------------------------------------------
878
    t => sections(is)%trac(:)
879
    !--------------------------------------------------------------------------------------------------------------------------
880
    DO iq=1, nt(is)                                                  !=== LOOP ON TRACERS
881
    !--------------------------------------------------------------------------------------------------------------------------
882
      tnam = TRIM(t(iq)%name)                                        !--- Original name
883
      IF(COUNT(t%name == tnam) == 1) CYCLE                           !--- Current tracer is not duplicated: finished
884
      tnam_new = TRIM(tnam)//'_'//TRIM(sections(is)%name)            !--- Same with section extension
885
      nq = SUM(nt(1:is-1))                                           !--- Number of tracers in previous sections
886
      ns = nt(is)                                                    !--- Number of tracers in the current section
887
      tr(iq + nq)%name = TRIM(tnam_new)                              !--- Modify tracer name
888
      WHERE(tr(1+nq:ns+nq)%parent==tnam) tr(1+nq:ns+nq)%parent=tnam_new  !--- Modify parent name
889
    !--------------------------------------------------------------------------------------------------------------------------
890
    END DO
891
  !----------------------------------------------------------------------------------------------------------------------------
892
  END DO
893
  !----------------------------------------------------------------------------------------------------------------------------
894
  CALL sortTracers(tr)
895
END FUNCTION cumulTracers
896
!==============================================================================================================================
897
898
!==============================================================================================================================
899
1
SUBROUTINE setDirectKeys(tr)
900
  TYPE(trac_type), INTENT(INOUT) :: tr(:)
901
902
  !--- Update %iqParent, %iqDescen, %nqDescen, %nqChildren
903
1
  CALL indexUpdate(tr)
904
905
  !--- Extract some direct-access keys
906
!  DO iq = 1, SIZE(tr)
907
!    tr(iq)%keys%<key> = getKey_prv(it, "<key>", tr%keys, <default_value> )
908
!  END DO
909
1
END SUBROUTINE setDirectKeys
910
!==============================================================================================================================
911
912
!==============================================================================================================================
913
2
LOGICAL FUNCTION dispTraSection(message, sname, modname) RESULT(lerr)
914
  CHARACTER(LEN=*), INTENT(IN) :: message, sname, modname
915
  INTEGER :: idb, iq, nq
916
2
  INTEGER, ALLOCATABLE :: hadv(:), vadv(:)
917
2
  CHARACTER(LEN=maxlen), ALLOCATABLE :: phas(:), prnt(:)
918
  TYPE(trac_type), POINTER :: tm(:)
919
2
  lerr = .FALSE.
920

4
  idb = strIdx(dBase(:)%name, sname); IF(idb == 0) RETURN
921
2
  tm => dBase(idb)%trac
922
2
  nq = SIZE(tm)
923
  !--- BEWARE ! Can't use the "getKeyByName" functions yet.
924
  !             Names must first include the phases for tracers defined on multiple lines.
925




24
  hadv = str2int(fgetKeys('hadv',  tm(:)%keys, '10'))
926



24
  vadv = str2int(fgetKeys('vadv',  tm(:)%keys, '10'))
927



24
  prnt =         fgetKeys('parent',tm(:)%keys,  '' )
928





23
  IF(getKey('phases', phas, ky=tm(:)%keys)) phas = fGetKeys('phase', tm(:)%keys, 'g')
929
2
  CALL msg(TRIM(message)//':', modname)
930

12
  IF(ALL(prnt == 'air')) THEN
931




46
    IF(test(dispTable('iiiss',   ['iq    ','hadv  ','vadv  ','name  ','phase '],                   cat(tm%name,       phas),  &
932
4
                 cat([(iq, iq=1, nq)], hadv, vadv),                 nColMax=maxTableWidth, nHead=2, sub=modname), lerr)) RETURN
933
  ELSE IF(ALL(tm%iGeneration == -1)) THEN
934
    IF(test(dispTable('iiisss', ['iq    ','hadv  ','vadv  ','name  ','parent','phase '],           cat(tm%name, prnt, phas),  &
935
                 cat([(iq, iq=1, nq)], hadv, vadv),                 nColMax=maxTableWidth, nHead=2, sub=modname), lerr)) RETURN
936
  ELSE
937
    IF(test(dispTable('iiissis', ['iq    ','hadv  ','vadv  ','name  ','parent','igen  ','phase '], cat(tm%name, prnt, phas),  &
938
                 cat([(iq, iq=1, nq)], hadv, vadv, tm%iGeneration), nColMax=maxTableWidth, nHead=2, sub=modname), lerr)) RETURN
939
  END IF
940


2
END FUNCTION dispTraSection
941
!==============================================================================================================================
942
943
944
!==============================================================================================================================
945
!== CREATE A SCALAR ALIAS OF THE COMPONENT OF THE TRACERS DESCRIPTOR "t" NAMED "tname" ========================================
946
!==============================================================================================================================
947
FUNCTION aliasTracer(tname, t) RESULT(out)
948
  TYPE(trac_type),         POINTER    :: out
949
  CHARACTER(LEN=*),        INTENT(IN) :: tname
950
  TYPE(trac_type), TARGET, INTENT(IN) :: t(:)
951
  INTEGER :: it
952
  it = strIdx(t(:)%name, tname)
953
  out => NULL(); IF(it /= 0) out => t(it)
954
END FUNCTION aliasTracer
955
!==============================================================================================================================
956
957
958
!==============================================================================================================================
959
!=== FROM A LIST OF INDEXES OR NAMES, CREATE A SUBSET OF THE TRACERS DESCRIPTORS LIST "trac" ==================================
960
!==============================================================================================================================
961
FUNCTION trSubset_Indx(trac,idx) RESULT(out)
962
  TYPE(trac_type), ALLOCATABLE             ::  out(:)
963
  TYPE(trac_type), ALLOCATABLE, INTENT(IN) :: trac(:)
964
  INTEGER,                      INTENT(IN) ::  idx(:)
965
  out = trac(idx)
966
  CALL indexUpdate(out)
967
END FUNCTION trSubset_Indx
968
!------------------------------------------------------------------------------------------------------------------------------
969
FUNCTION trSubset_Name(trac,nam) RESULT(out)
970
  TYPE(trac_type), ALLOCATABLE             ::  out(:)
971
  TYPE(trac_type), ALLOCATABLE, INTENT(IN) :: trac(:)
972
  CHARACTER(LEN=*),             INTENT(IN) ::  nam(:)
973
  out = trac(strIdx(trac(:)%name, nam))
974
  CALL indexUpdate(out)
975
END FUNCTION trSubset_Name
976
!==============================================================================================================================
977
978
979
!==============================================================================================================================
980
!=== CREATE THE SUBSET OF THE TRACERS DESCRIPTORS LIST "trac" HAVING THE FIRST GENERATION ANCESTOR NAMED "nam" ================
981
!==============================================================================================================================
982
FUNCTION trSubset_gen0Name(trac,nam) RESULT(out)
983
  TYPE(trac_type), ALLOCATABLE             ::  out(:)
984
  TYPE(trac_type), ALLOCATABLE, INTENT(IN) :: trac(:)
985
  CHARACTER(LEN=*),             INTENT(IN) ::  nam
986
  out = trac(strFind(delPhase(trac(:)%gen0Name), nam))
987
  CALL indexUpdate(out)
988
END FUNCTION trSubset_gen0Name
989
!==============================================================================================================================
990
991
992
!==============================================================================================================================
993
!=== UPDATE THE INDEXES iqParent, iqDescend AND iGeneration IN THE TRACERS DESCRIPTOR LIST "tr" (USEFULL FOR SUBSETS) =========
994
!==============================================================================================================================
995
2
SUBROUTINE indexUpdate(tr)
996
  TYPE(trac_type), INTENT(INOUT) :: tr(:)
997
4
  INTEGER :: iq, ig, igen, ngen, ix(SIZE(tr))
998


32
  tr(:)%iqParent = strIdx( tr(:)%name, tr(:)%parent )                !--- Parent index
999
12
  DO iq = 1, SIZE(tr); CALL addKey_1('iqParent', int2str(tr(iq)%iqParent), tr(iq)%keys); END DO
1000
12
  ngen = MAXVAL(tr(:)%iGeneration, MASK=.TRUE.)
1001
12
  DO iq = 1, SIZE(tr)
1002
10
    ig = tr(iq)%iGeneration
1003
10
    IF(ALLOCATED(tr(iq)%iqDescen)) DEALLOCATE(tr(iq)%iqDescen)
1004

10
    ALLOCATE(tr(iq)%iqDescen(0))
1005
10
    CALL idxAncestor(tr, ix, ig)                                     !--- Ancestor of generation "ng" for each tr
1006
10
    DO igen = ig+1, ngen
1007
      tr(iq)%iqDescen = [tr(iq)%iqDescen, find(ix==iq .AND. tr%iGeneration==igen)]
1008
      tr(iq)%nqDescen = SIZE(tr(iq)%iqDescen)
1009
10
      IF(igen == ig+1) THEN
1010
        tr(iq)%nqChildren = tr(iq)%nqDescen
1011
        CALL addKey_1('nqChildren', int2str(tr(iq)%nqChildren), tr(iq)%keys)
1012
      END IF
1013
    END DO
1014

10
    CALL addKey_1('iqDescen', strStack(int2str(tr(iq)%iqDescen)), tr(iq)%keys)
1015
12
    CALL addKey_1('nqDescen',          int2str(tr(iq)%nqDescen) , tr(iq)%keys)
1016
  END DO
1017
2
END SUBROUTINE indexUpdate
1018
!==============================================================================================================================
1019
1020
1021
!==============================================================================================================================
1022
!=== READ FILE "fnam" TO APPEND THE "dBase" TRACERS DATABASE WITH AS MUCH SECTIONS AS PARENTS NAMES IN "isot(:)%parent":   ====
1023
!===  * Each section dBase(i)%name contains the isotopes "dBase(i)%trac(:)" descending on "dBase(i)%name"="iso(i)%parent"  ====
1024
!===  * For each isotopes class, the <key>=<val> vector of each tracer is moved into the isotopes descriptor "isot"        ====
1025
!=== NOTES:                                                                                                                ====
1026
!===  * Most of the "isot" components have been defined in the calling routine (readIsotopes):                             ====
1027
!===      parent,  nzone, zone(:),  niso, keys(:)%name,  ntiso, trac(:),  nphas, phas,  iqIsoPha(:,:),  itZonPhi(:,:)      ====
1028
!===  * Same syntax for isotopes file and "tracer.def": a tracers section contains one line for each of its isotopes       ====
1029
!===  * Each tracers section can contain a "params" virtual isotope line of isotopes parameters default values             ====
1030
!===  * In case keys are found both in the "params" section and the "*.def" file, the later value is retained              ====
1031
!===  * On each isotope line, defined keys can be used for other keys defintions (single level depth substitution)         ====
1032
!===  * The routine gives an error if a required isotope is not available in the database stored in "fnam"                 ====
1033
!==============================================================================================================================
1034
LOGICAL FUNCTION readIsotopesFile_prv(fnam, isot) RESULT(lerr)
1035
  CHARACTER(LEN=*),        INTENT(IN)    :: fnam                     !--- Input file name
1036
  TYPE(isot_type), TARGET, INTENT(INOUT) :: isot(:)                  !--- Isotopes descriptors (field %parent must be defined!)
1037
  LOGICAL :: lFound
1038
  INTEGER :: is, iis, it, idb, ndb, nb0
1039
  CHARACTER(LEN=maxlen), ALLOCATABLE :: vals(:)
1040
  CHARACTER(LEN=maxlen)              :: modname
1041
  TYPE(trac_type),           POINTER ::   tt(:), t
1042
  TYPE(dataBase_type),   ALLOCATABLE ::  tdb(:)
1043
  modname = 'readIsotopesFile'
1044
1045
  !--- THE INPUT FILE MUST BE PRESENT
1046
  INQUIRE(FILE=TRIM(fnam), EXIST=lFound); lerr = .NOT.lFound
1047
  IF(fmsg('Missing isotopes parameters file "'//TRIM(fnam)//'"', modname, lerr)) RETURN
1048
1049
  !--- READ THE FILE SECTIONS, ONE EACH PARENT TRACER
1050
  nb0 = SIZE(dBase, DIM=1)+1                                         !--- Next database element index
1051
  IF(test(readSections(fnam,strStack(isot(:)%parent,'|')),lerr)) RETURN !--- Read sections, one each parent tracer
1052
  ndb = SIZE(dBase, DIM=1)                                           !--- Current database size
1053
  DO idb = nb0, ndb
1054
    iis = idb-nb0+1
1055
1056
    !--- GET FEW GLOBAL KEYS FROM "def" FILES AND ADD THEM TO THE 'params' SECTION
1057
    CALL addKeysFromDef(dBase(idb)%trac, 'params')
1058
1059
    !--- SUBSTITUTE THE KEYS DEFINED IN THE 'params' VIRTUAL TRACER ; SUBSTITUTE LOCAL KEYS ; REMOVE 'params' VIRTUAL TRACER
1060
    CALL subDefault(dBase(idb)%trac, 'params', .TRUE.)
1061
1062
    tt => dBase(idb)%trac
1063
1064
    !--- REDUCE THE EXPRESSIONS TO OBTAIN SCALARS AND TRANSFER THEM TO THE "isot" ISOTOPES DESCRIPTORS VECTOR
1065
    DO it = 1, SIZE(dBase(idb)%trac)
1066
      t => dBase(idb)%trac(it)
1067
      is = strIdx(isot(iis)%keys(:)%name, t%name)                    !--- Index in "isot(iis)%keys(:)%name" of isotope "t%name"
1068
      IF(is == 0) CYCLE
1069
      IF(test(ANY(reduceExpr(t%keys%val, vals)), lerr)) RETURN       !--- Reduce expressions ; detect non-numerical elements
1070
      isot(iis)%keys(is)%key = t%keys%key
1071
      isot(iis)%keys(is)%val = vals
1072
    END DO
1073
1074
    !--- CHECK FOR MISSING ISOTOPES (NO KEYS ALLOCATED)
1075
    IF(test(checkList(isot(iis)%keys(:)%name, .NOT.[( ALLOCATED(isot(iis)%keys(is)%key), is=1, SIZE(isot(iis)%keys) )], &
1076
      'Check file "'//TRIM(fnam)//'" in section "'//TRIM(dBase(idb)%name)//'"', 'isotopes', 'missing'), lerr)) RETURN
1077
  END DO
1078
1079
  !--- CLEAN THE DATABASE ENTRIES
1080
  IF(nb0 == 1) THEN
1081
    DEALLOCATE(dBase); ALLOCATE(dBase(0))
1082
  ELSE
1083
    ALLOCATE(tdb(nb0-1)); tdb(1:nb0-1)=dBase(1:nb0-1); CALL MOVE_ALLOC(FROM=tdb, TO=dBase)
1084
  END IF
1085
1086
  !--- GET THE isoCheck ENTRY FROM THE *.DEF FILES (MIGHT BE CHANGED TO A CLASS-DEPENDANT KEYWORD)
1087
  CALL get_in('ok_iso_verif', isot(strIdx(isot%parent, 'H2O'))%check, .FALSE.)
1088
1089
  lerr = dispIsotopes()
1090
1091
CONTAINS
1092
1093
!------------------------------------------------------------------------------------------------------------------------------
1094
LOGICAL FUNCTION dispIsotopes() RESULT(lerr)
1095
  INTEGER :: ik, nk, ip, it, nt
1096
  CHARACTER(LEN=maxlen) :: prf
1097
  CHARACTER(LEN=maxlen), ALLOCATABLE :: ttl(:), val(:,:)
1098
  CALL msg('Isotopes parameters read from file "'//TRIM(fnam)//'":', modname)
1099
  DO ip = 1, SIZE(isot)                                              !--- Loop on parents tracers
1100
    nk = SIZE(isot(ip)%keys(1)%key)                                  !--- Same keys for each isotope
1101
    nt = SIZE(isot(ip)%keys)                                         !--- Number of isotopes
1102
    prf = 'i'//REPEAT('s',nk+1)                                      !--- Profile for table printing
1103
    ALLOCATE(ttl(nk+2), val(nt,nk+1))
1104
    ttl(1:2) = ['it  ','name']; ttl(3:nk+2) = isot(ip)%keys(1)%key(:)!--- Titles line with keys names
1105
    val(:,1) = isot(ip)%keys(:)%name                                 !--- Values table 1st column: isotopes names
1106
    DO ik = 1, nk
1107
      DO it = 1, nt
1108
        val(it,ik+1) = isot(ip)%keys(it)%val(ik)                     !--- Other columns: keys values
1109
      END DO
1110
    END DO
1111
    IF(test(fmsg('Problem with the table content', modname, dispTable(prf, ttl, val, &
1112
            cat([(it,it=1,nt)]), rFmt='(EN8.4)', nColMax=maxTableWidth, nHead=2, sub=modname)), lerr)) RETURN
1113
    DEALLOCATE(ttl, val)
1114
  END DO
1115
END FUNCTION dispIsotopes
1116
!------------------------------------------------------------------------------------------------------------------------------
1117
1118
END FUNCTION readIsotopesFile_prv
1119
!==============================================================================================================================
1120
1121
1122
!==============================================================================================================================
1123
!=== IF ISOTOPES (2ND GENERATION TRACERS) ARE DETECTED:                                                                     ===
1124
!===    * COMPUTE MOST OF THE RELATED QUANTITIES ("isot" COMPONENTS).                                                       ===
1125
!===    * COMPUTE FEW ISOTOPES-DEDICATED "trac" COMPONENTS                                                                  ===
1126
!===    * CALL readIsotopesFile_prv TO GET PHYSICAL QUANTITIES (<key>=<val> PAIRS)                                          ===
1127
!===      NOTE: THIS IS DONE HERE (IN A ROUTINE CALLED BY THE DYNAMIC), BECAUSE THE DYNAMIC NEEDS FEW PHYSICAL PARAMETERS.  ===
1128
!==============================================================================================================================
1129

1
LOGICAL FUNCTION readIsotopesFile(iNames) RESULT(lerr)
1130
  CHARACTER(LEN=maxlen), TARGET, OPTIONAL, INTENT(IN)  :: iNames(:)
1131
1
  CHARACTER(LEN=maxlen), ALLOCATABLE :: p(:), str(:)                 !--- Temporary storage
1132
  CHARACTER(LEN=maxlen) :: iName, modname
1133
  CHARACTER(LEN=1)   :: ph                                           !--- Phase
1134
  INTEGER :: ic, ip, iq, it, iz
1135
1
  LOGICAL, ALLOCATABLE :: ll(:)                                      !--- Mask
1136
  TYPE(trac_type), POINTER   ::  t(:), t1
1137
  TYPE(isot_type), POINTER   ::  i
1138
1
  lerr = .FALSE.
1139
1
  modname = 'readIsotopesFile'
1140
1141
1
  t => tracers
1142
1143
  !--- GET FROM "tracers" THE FULL LIST OF AVAILABLE ISOTOPES CLASSES
1144



11
  p = PACK(delPhase(t%parent), MASK = t%type=='tracer' .AND. t%iGeneration==1)
1145
1
  CALL strReduce(p, nbIso)
1146
1147
  !--- CHECK WHETHER NEEDED ISOTOPES CLASSES "iNames" ARE AVAILABLE OR NOT
1148

1
  IF(PRESENT(iNames)) THEN
1149
    DO it = 1, SIZE(iNames)
1150
      IF(test(fmsg('No isotopes class "'//TRIM(iNames(it))//'" found among tracers', modname, ALL(p /= iNames(it))), lerr)) RETURN
1151
    END DO
1152
    p = iNames; nbIso = SIZE(p)
1153
  END IF
1154





1
  IF(ALLOCATED(isotopes)) DEALLOCATE(isotopes)
1155






1
  ALLOCATE(isotopes(nbIso))
1156
1157
1
  IF(nbIso==0) RETURN                                                !=== NO ISOTOPES: FINISHED
1158
1159
  !--- ISOTOPES RELATED VARIABLES ; NULL OR EMPTY IF NO ISOTOPES
1160
  isotopes(:)%parent = p
1161
  DO ic = 1, SIZE(p)                                                 !--- Loop on isotopes classes
1162
    i => isotopes(ic)
1163
    iname = i%parent                                                 !--- Current isotopes class name (parent tracer name)
1164
1165
    !=== Isotopes children of tracer "iname": mask, names, number (same for each phase of "iname")
1166
    ll = t(:)%type=='tracer' .AND. delPhase(t(:)%parent) == iname .AND. t(:)%phase == 'g'
1167
    str = PACK(delPhase(t(:)%name), MASK = ll)                       !--- Effectively found isotopes of "iname"
1168
    i%niso = SIZE(str)                                               !--- Number of "effectively found isotopes of "iname"
1169
    ALLOCATE(i%keys(i%niso))
1170
    FORALL(it = 1:i%niso) i%keys(it)%name = str(it)
1171
1172
    !=== Geographic tagging tracers descending on tracer "iname": mask, names, number
1173
    ll = t(:)%type=='tag'    .AND. delPhase(t(:)%gen0Name) == iname .AND. t(:)%iGeneration == 2
1174
    i%zone = PACK(strTail(t(:)%name,'_',.TRUE.), MASK = ll)          !--- Tagging zones names  for isotopes category "iname"
1175
    CALL strReduce(i%zone)
1176
    i%nzone = SIZE(i%zone)                                           !--- Tagging zones number for isotopes category "iname"
1177
1178
    !=== Geographic tracers of the isotopes children of tracer "iname" (same for each phase of "iname")
1179
    !    NOTE: One might like to create a similar variable for 2nd generation tagging tracers (tagging the gen1 tracers)
1180
    str = PACK(delPhase(t(:)%name), MASK=ll)
1181
    CALL strReduce(str)
1182
    i%ntiso = i%niso + SIZE(str)                                     !--- Number of isotopes + their geographic tracers [ntiso]
1183
    ALLOCATE(i%trac(i%ntiso))
1184
    FORALL(it = 1:i%niso) i%trac(it) = i%keys(it)%name
1185
    FORALL(it = i%niso+1:i%ntiso) i%trac(it) = str(it-i%niso)
1186
1187
    !=== Phases for tracer "iname"
1188
    i%phase = ''
1189
    DO ip = 1, nphases; ph = known_phases(ip:ip); IF(strIdx(t%name,addPhase(iname, ph)) /= 0) i%phase = TRIM(i%phase)//ph; END DO
1190
    i%nphas = LEN_TRIM(i%phase)                                       !--- Equal to "nqo" for water
1191
1192
    !=== Tables giving the index in a table of effectively found items for each dynamical tracer (1<=iq<=nqtot)
1193
    DO iq = 1, SIZE(t)
1194
      t1 => tracers(iq)
1195
      IF(delPhase(t1%gen0Name)/=iname .OR. t1%iGeneration==0) CYCLE  !--- Only deal with tracers descending on "iname"
1196
      t1%iso_iGroup = ic                                             !--- Isotopes family       idx in list "isotopes(:)%parent"
1197
      t1%iso_iName  = strIdx(i%trac, strHead(delPhase(t1%name),'_',.TRUE.)) !--- Current isotope       idx in effective isotopes list
1198
      t1%iso_iZone  = strIdx(i%zone,          strTail(t1%name, '_',.TRUE.)) !--- Current isotope zone  idx in effective zones    list
1199
      t1%iso_iPhase =  INDEX(i%phase,TRIM(t1%phase))                 !--- Current isotope phase idx in effective phases   list
1200
      IF(t1%iGeneration /= 2) t1%iso_iZone = 0                       !--- Skip possible generation 1 tagging tracers
1201
    END DO
1202
1203
    !=== Table used to get iq (index in dyn array, size nqtot) from the isotope and phase indexes ; the full isotopes list
1204
    !    (including tagging tracers) is sorted this way:  iso1, iso2, ..., iso1_zone1, iso2_zone1, ..., iso1_zoneN, iso2_zoneN
1205
    i%iqIsoPha = RESHAPE( [( (strIdx(t%name,  addPhase(i%trac(it),i%phase(ip:ip))),       it=1, i%ntiso), ip=1, i%nphas)], &
1206
                         [i%ntiso, i%nphas] )
1207
    !=== Table used to get ix (index in tagging tracers isotopes list, size ntiso) from the zone and isotope indexes
1208
    i%itZonIso = RESHAPE( [( (strIdx(i%trac(:), TRIM(i%trac(it))//'_'//TRIM(i%zone(iz))), iz=1, i%nzone), it=1, i%niso )], &
1209
                         [i%nzone, i%niso] )
1210
  END DO
1211
1212
  !=== READ PHYSICAL PARAMETERS FROM isoFile FILE
1213
  IF(test(readIsotopesFile_prv(isoFile, isotopes), lerr)) RETURN
1214
1215
  !=== CHECK CONSISTENCY
1216
  IF(test(testIsotopes(), lerr)) RETURN
1217
1218
  !=== SELECT FIRST ISOTOPES CLASS OR, IF POSSIBLE, WATER CLASS
1219


1
  IF(.NOT.test(isoSelect(1, .TRUE.), lerr)) THEN; IF(isotope%parent == 'H2O') iH2O = ixIso; END IF
1220
1221
CONTAINS
1222
1223
!------------------------------------------------------------------------------------------------------------------------------
1224
LOGICAL FUNCTION testIsotopes() RESULT(lerr)     !--- MAKE SURE MEMBERS OF AN ISOTOPES FAMILY ARE PRESENT IN THE SAME PHASES
1225
!------------------------------------------------------------------------------------------------------------------------------
1226
  INTEGER :: ix, it, ip, np, iz, nz
1227
  TYPE(isot_type), POINTER :: i
1228
  DO ix = 1, nbIso
1229
    i => isotopes(ix)
1230
    !--- Check whether each isotope and tagging isotopic tracer is present in the same number of phases
1231
    DO it = 1, i%ntiso
1232
      np = SUM([(COUNT(tracers(:)%name == addPhase(i%trac(it), i%phase(ip:ip))), ip=1, i%nphas)])
1233
      IF(test(fmsg(TRIM(int2str(np))//' phases instead of '//TRIM(int2str(i%nphas))//' for '//TRIM(i%trac(it)), &
1234
        modname, np /= i%nphas), lerr)) RETURN
1235
    END DO
1236
    DO it = 1, i%niso
1237
      nz = SUM([(COUNT(i%trac == TRIM(i%trac(it))//'_'//i%zone(iz)), iz=1, i%nzone)])
1238
      IF(test(fmsg(TRIM(int2str(nz))//' tagging zones instead of '//TRIM(int2str(i%nzone))//' for '//TRIM(i%trac(it)), &
1239
        modname, nz /= i%nzone), lerr)) RETURN
1240
    END DO
1241
  END DO
1242
END FUNCTION testIsotopes
1243
!------------------------------------------------------------------------------------------------------------------------------
1244
1245
END FUNCTION readIsotopesFile
1246
!==============================================================================================================================
1247
1248
1249
!==============================================================================================================================
1250
!=== THE ROUTINE isoSelect IS USED TO SWITCH FROM AN ISOTOPE FAMILY TO ANOTHER: ISOTOPES DEPENDENT PARAMETERS ARE UPDATED
1251
!     Single generic "isoSelect" routine, using the predefined index of the parent (fast version) or its name (first call).
1252
!==============================================================================================================================
1253
LOGICAL FUNCTION isoSelectByName(iName, lVerbose) RESULT(lerr)
1254
   IMPLICIT NONE
1255
   CHARACTER(LEN=*),  INTENT(IN) :: iName
1256
   LOGICAL, OPTIONAL, INTENT(IN) :: lVerbose
1257
   INTEGER :: iIso
1258
   LOGICAL :: lV
1259
   lV = .FALSE.; IF(PRESENT(lVerbose)) lV = lVerbose
1260
   iIso = strIdx(isotopes(:)%parent, iName)
1261
   IF(test(iIso == 0, lerr)) THEN
1262
      niso = 0; ntiso = 0; nzone = 0; nphas = 0; isoCheck=.FALSE.
1263
      CALL msg('no isotope family named "'//TRIM(iName)//'"', ll=lV)
1264
      RETURN
1265
   END IF
1266
   lerr = isoSelectByIndex(iIso, lV)
1267
END FUNCTION isoSelectByName
1268
!==============================================================================================================================
1269
LOGICAL FUNCTION isoSelectByIndex(iIso, lVerbose) RESULT(lerr)
1270
   IMPLICIT NONE
1271
   INTEGER,           INTENT(IN) :: iIso
1272
   LOGICAL, OPTIONAL, INTENT(IN) :: lVerbose
1273
   LOGICAL :: lV
1274
   lv = .FALSE.; IF(PRESENT(lVerbose)) lv = lVerbose
1275
   lerr = .FALSE.
1276
   IF(iIso == ixIso) RETURN                                          !--- Nothing to do if the index is already OK
1277
   lerr = iIso<=0 .OR. iIso>SIZE(isotopes)
1278
   CALL msg('Inconsistent isotopes family index '//TRIM(int2str(iIso))//': should be > 0 and <= '&
1279
          //TRIM(int2str(SIZE(isotopes)))//'"', ll = lerr .AND. lV)
1280
   IF(lerr) RETURN
1281
   ixIso = iIso                                                      !--- Update currently selected family index
1282
   isotope  => isotopes(ixIso)                                       !--- Select corresponding component
1283
   isoKeys  => isotope%keys;     niso     = isotope%niso
1284
   isoName  => isotope%trac;     ntiso    = isotope%ntiso
1285
   isoZone  => isotope%zone;     nzone    = isotope%nzone
1286
   isoPhas  => isotope%phase;    nphas    = isotope%nphas
1287
   itZonIso => isotope%itZonIso; isoCheck = isotope%check
1288
   iqIsoPha => isotope%iqIsoPha
1289
END FUNCTION isoSelectByIndex
1290
!==============================================================================================================================
1291
1292
1293
!==============================================================================================================================
1294
!=== ADD THE <key>=<val> PAIR TO THE "ky[(:)]" KEY[S] DESCRIPTOR[S] OR THE <key>=<val(:)> PAIRS TO THE "ky(:)" KEYS DESCRIPTORS
1295
!==============================================================================================================================
1296
125
SUBROUTINE addKey_1(key, val, ky, lOverWrite)
1297
  CHARACTER(LEN=*),  INTENT(IN)    :: key, val
1298
  TYPE(keys_type),   INTENT(INOUT) :: ky
1299
  LOGICAL, OPTIONAL, INTENT(IN)    :: lOverWrite
1300
!------------------------------------------------------------------------------------------------------------------------------
1301
  CHARACTER(LEN=maxlen), ALLOCATABLE :: k(:), v(:)
1302
  INTEGER :: iky, nky
1303
  LOGICAL :: lo
1304
30
  lo=.TRUE.; IF(PRESENT(lOverWrite)) lo=lOverWrite
1305
125
  IF(.NOT.ALLOCATED(ky%key)) THEN
1306
    ALLOCATE(ky%key(1)); ky%key(1)=key
1307
    ALLOCATE(ky%val(1)); ky%val(1)=val
1308
    RETURN
1309
  END IF
1310
125
  iky = strIdx(ky%key,key)
1311
125
  IF(iky == 0) THEN
1312
56
    nky = SIZE(ky%key)
1313





900
    ALLOCATE(k(nky+1)); k(1:nky) = ky%key; k(nky+1) = key; ky%key = k
1314




900
    ALLOCATE(v(nky+1)); v(1:nky) = ky%val; v(nky+1) = val; ky%val = v
1315
69
  ELSE IF(lo) THEN
1316

55
    ky%key(iky) = key; ky%val(iky) = val
1317
  END IF
1318

250
END SUBROUTINE addKey_1
1319
!==============================================================================================================================
1320
SUBROUTINE addKey_m(key, val, ky, lOverWrite)
1321
  CHARACTER(LEN=*),  INTENT(IN)    :: key, val
1322
  TYPE(keys_type),   INTENT(INOUT) :: ky(:)
1323
  LOGICAL, OPTIONAL, INTENT(IN)    :: lOverWrite
1324
!------------------------------------------------------------------------------------------------------------------------------
1325
  INTEGER :: itr
1326
  DO itr = 1, SIZE(ky)
1327
    CALL addKey_1(key, val, ky(itr), lOverWrite)
1328
  END DO
1329
END SUBROUTINE addKey_m
1330
!==============================================================================================================================
1331
SUBROUTINE addKey_mm(key, val, ky, lOverWrite)
1332
  CHARACTER(LEN=*),  INTENT(IN)    :: key, val(:)
1333
  TYPE(keys_type),   INTENT(INOUT) :: ky(:)
1334
  LOGICAL, OPTIONAL, INTENT(IN)    :: lOverWrite
1335
!------------------------------------------------------------------------------------------------------------------------------
1336
  INTEGER :: itr
1337
  DO itr = 1, SIZE(ky); CALL addKey_1(key, val(itr), ky(itr), lOverWrite); END DO
1338
END SUBROUTINE addKey_mm
1339
!==============================================================================================================================
1340
1341
1342
!==============================================================================================================================
1343
!=== OVERWRITE THE KEYS OF THE TRACER NAMED "tr0" WITH THE VALUES FOUND IN THE *.def FILES, IF ANY. ===========================
1344
!==============================================================================================================================
1345
SUBROUTINE addKeysFromDef(t, tr0)
1346
  TYPE(trac_type), ALLOCATABLE, INTENT(INOUT) :: t(:)
1347
  CHARACTER(LEN=*),             INTENT(IN)    :: tr0
1348
!------------------------------------------------------------------------------------------------------------------------------
1349
  CHARACTER(LEN=maxlen) :: val
1350
  INTEGER               :: ik, jd
1351
  jd = strIdx(t%name, tr0)
1352
  IF(jd == 0) RETURN
1353
  DO ik = 1, SIZE(t(jd)%keys%key)
1354
    CALL get_in(t(jd)%keys%key(ik), val, '*none*')
1355
    IF(val /= '*none*') CALL addKey_1(t(jd)%keys%key(ik), val, t(jd)%keys, .TRUE.)
1356
  END DO
1357
END SUBROUTINE addKeysFromDef
1358
!==============================================================================================================================
1359
1360
1361
!==============================================================================================================================
1362
!=== REMOVE THE KEYS NAMED "keyn(:)" FROM EITHER THE "itr"th OR ALL THE KEYS DESCRIPTORS OF "ky(:)" ===========================
1363
!==============================================================================================================================
1364

5
SUBROUTINE delKey_1(itr, keyn, ky)
1365
  INTEGER,          INTENT(IN)    :: itr
1366
  CHARACTER(LEN=*), INTENT(IN)    :: keyn(:)
1367
  TYPE(trac_type),  INTENT(INOUT) :: ky(:)
1368
!------------------------------------------------------------------------------------------------------------------------------
1369
  CHARACTER(LEN=maxlen), ALLOCATABLE :: k(:), v(:)
1370
5
  LOGICAL,               ALLOCATABLE :: ll(:)
1371
  INTEGER :: iky
1372

5
  IF(itr<=0 .OR. itr>SIZE(ky, DIM=1)) RETURN                          !--- Index is out of range
1373



200
  ll = [( ALL(keyn/=ky(itr)%keys%key(iky)), iky=1, SIZE(ky(itr)%keys%key) )]
1374
5
  k = PACK(ky(itr)%keys%key, MASK=ll); CALL MOVE_ALLOC(FROM=k, TO=ky(itr)%keys%key)
1375
5
  v = PACK(ky(itr)%keys%val, MASK=ll); CALL MOVE_ALLOC(FROM=v, TO=ky(itr)%keys%val)
1376
5
END SUBROUTINE delKey_1
1377
!==============================================================================================================================
1378

1
SUBROUTINE delKey(keyn, ky)
1379
  CHARACTER(LEN=*), INTENT(IN)    :: keyn(:)
1380
  TYPE(trac_type),  INTENT(INOUT) :: ky(:)
1381
!------------------------------------------------------------------------------------------------------------------------------
1382
  INTEGER :: iky
1383
6
  DO iky = 1, SIZE(ky); CALL delKey_1(iky, keyn, ky); END DO
1384
1
END SUBROUTINE delKey
1385
!==============================================================================================================================
1386
1387
1388
!==============================================================================================================================
1389
!================ GET THE VALUE OF A KEY FROM A "keys_type" DERIVED TYPE ; THE RESULT IS THE RETURNED VALUE ===================
1390
!==============================================================================================================================
1391
330
CHARACTER(LEN=maxlen) FUNCTION fgetKeyIdx_s1(itr, keyn, ky, def_val, lerr) RESULT(val)
1392
  INTEGER,                    INTENT(IN)  :: itr
1393
  CHARACTER(LEN=*),           INTENT(IN)  :: keyn
1394
  TYPE(keys_type),            INTENT(IN)  :: ky(:)
1395
  CHARACTER(LEN=*), OPTIONAL, INTENT(IN)  :: def_val
1396
  LOGICAL,          OPTIONAL, INTENT(OUT) :: lerr
1397
!------------------------------------------------------------------------------------------------------------------------------
1398
  INTEGER :: iky
1399
  LOGICAL :: ler
1400
165
  iky = 0; val = ''
1401

330
  IF(.NOT.test(itr <= 0 .OR. itr > SIZE(ky), ler)) iky = strIdx(ky(itr)%key(:), keyn)    !--- Correct index
1402
165
  IF(.NOT.test(iky == 0, ler))                     val = ky(itr)%val(iky)                !--- Found key
1403
165
  IF(iky == 0) THEN
1404

5
    IF(.NOT.test(.NOT.PRESENT(def_val), ler))      val = def_val                         !--- Default value
1405
  END IF
1406
165
  IF(PRESENT(lerr)) lerr = ler
1407
165
END FUNCTION fgetKeyIdx_s1
1408
!==============================================================================================================================
1409
CHARACTER(LEN=maxlen) FUNCTION fgetKeyNam_s1(tname, keyn, ky, def_val, lerr) RESULT(val)
1410
  CHARACTER(LEN=*),           INTENT(IN)  :: tname, keyn
1411
  TYPE(keys_type),            INTENT(IN)  :: ky(:)
1412
  CHARACTER(LEN=*), OPTIONAL, INTENT(IN)  :: def_val
1413
  LOGICAL,          OPTIONAL, INTENT(OUT) :: lerr
1414
!------------------------------------------------------------------------------------------------------------------------------
1415
  val = fgetKeyIdx_s1(strIdx(ky(:)%name, tname), keyn, ky, def_val, lerr)
1416
END FUNCTION fgetKeyNam_s1
1417
!==============================================================================================================================
1418
14
FUNCTION fgetKeys(keyn, ky, def_val, lerr) RESULT(val)
1419
CHARACTER(LEN=maxlen),        ALLOCATABLE :: val(:)
1420
  CHARACTER(LEN=*),           INTENT(IN)  :: keyn
1421
  TYPE(keys_type),            INTENT(IN)  :: ky(:)
1422
  CHARACTER(LEN=*), OPTIONAL, INTENT(IN)  :: def_val
1423
  LOGICAL,          OPTIONAL, INTENT(OUT) :: lerr
1424
!------------------------------------------------------------------------------------------------------------------------------
1425
28
  LOGICAL :: ler(SIZE(ky))
1426
  INTEGER :: it
1427






287
  val = [(fgetKeyIdx_s1(it, keyn, ky, def_val, ler(it)), it = 1, SIZE(ky))]
1428

51
  IF(PRESENT(lerr)) lerr = ANY(ler)
1429
14
END FUNCTION fgetKeys
1430
!==============================================================================================================================
1431
1432
1433
!==============================================================================================================================
1434
!========== GET THE VALUE OF A KEY FROM A "keys_type" DERIVED TYPE ; THE RETURNED VALUE IS THE ERROR CODE        ==============
1435
!==========  The key "keyn" is searched in: 1)           "ky(:)%name" (if given)                                 ==============
1436
!==========                                 2)      "tracers(:)%name"                                            ==============
1437
!==========                                 3) "isotope%keys(:)%name"                                            ==============
1438
!==========  for the tracer[s] "tname[(:)]" (if given) or all the available tracers from the used set otherwise. ==============
1439
!==========  The type of the returned value(s) can be string, integer or real, scalar or vector                  ==============
1440
!==============================================================================================================================
1441
LOGICAL FUNCTION getKeyByName_s1(keyn, val, tname, ky) RESULT(lerr)
1442
  CHARACTER(LEN=*),          INTENT(IN)  :: keyn
1443
  CHARACTER(LEN=maxlen),     INTENT(OUT) :: val
1444
  CHARACTER(LEN=*),          INTENT(IN)  :: tname
1445
  TYPE(keys_type), OPTIONAL, INTENT(IN)  :: ky(:)
1446
!------------------------------------------------------------------------------------------------------------------------------
1447
  CHARACTER(LEN=maxlen) :: tnam
1448
  tnam = strHead(delPhase(tname),'_',.TRUE.)                                             !--- Remove phase and tag
1449
  IF(PRESENT(ky)) THEN                                                                   !=== KEY FROM "ky"
1450
               val = fgetKeyNam_s1(tname, keyn, ky,           lerr=lerr)                 !--- "ky" and "tname"
1451
    IF( lerr ) val = fgetKeyNam_s1(tnam,  keyn, ky,           lerr=lerr)                 !--- "ky" and "tnam"
1452
  ELSE
1453
    IF(         .NOT.test(.NOT.ALLOCATED(tracers ), lerr)) lerr = SIZE(tracers ) == 0    !=== KEY FROM "tracers"
1454
    IF(.NOT.lerr) THEN
1455
               val = fgetKeyNam_s1(tname, keyn, tracers%keys, lerr=lerr)                 !--- "ky" and "tname"
1456
      IF(lerr) val = fgetKeyNam_s1(tnam,  keyn, tracers%keys, lerr=lerr)                 !--- "ky" and "tnam"
1457
    END IF
1458
    IF(lerr.AND..NOT.test(.NOT.ALLOCATED(isotopes), lerr)) lerr = SIZE(isotopes) == 0    !=== KEY FROM "isotope"
1459
    IF(.NOT.lerr) THEN
1460
               val = fgetKeyNam_s1(tname, keyn, isotope%keys, lerr=lerr)                 !--- "ky" and "tname"
1461
      IF(lerr) val = fgetKeyNam_s1(tnam,  keyn, isotope%keys, lerr=lerr)                 !--- "ky" and "tnam"
1462
    END IF
1463
  END IF
1464
END FUNCTION getKeyByName_s1
1465
!==============================================================================================================================
1466
LOGICAL FUNCTION getKeyByName_s1m(keyn, val, tname, ky) RESULT(lerr)
1467
  CHARACTER(LEN=*),                   INTENT(IN)  :: keyn
1468
  CHARACTER(LEN=maxlen), ALLOCATABLE, INTENT(OUT) :: val(:)
1469
  CHARACTER(LEN=*),                   INTENT(IN)  :: tname
1470
  TYPE(keys_type),          OPTIONAL, INTENT(IN)  :: ky(:)
1471
!------------------------------------------------------------------------------------------------------------------------------
1472
  CHARACTER(LEN=maxlen) :: sval
1473
  lerr = getKeyByName_s1(keyn, sval, tname, ky)
1474
  IF(test(fmsg('missing key "'//TRIM(keyn)//'" for tracer "'//TRIM(tname)//'"', modname, lerr), lerr)) RETURN
1475
  lerr = strParse(sval, ',', val)
1476
END FUNCTION getKeyByName_s1m
1477
!==============================================================================================================================
1478
LOGICAL FUNCTION getKeyByName_sm(keyn, val, tname, ky, nam) RESULT(lerr)
1479
  CHARACTER(LEN=*),                             INTENT(IN)  :: keyn
1480
  CHARACTER(LEN=maxlen),           ALLOCATABLE, INTENT(OUT) :: val(:)
1481
  CHARACTER(LEN=*),                             INTENT(IN)  :: tname(:)
1482
  TYPE(keys_type),       OPTIONAL, TARGET,      INTENT(IN)  :: ky(:)
1483
  CHARACTER(LEN=maxlen), OPTIONAL, ALLOCATABLE, INTENT(OUT) :: nam(:)
1484
!------------------------------------------------------------------------------------------------------------------------------
1485
  TYPE(keys_type), POINTER ::  keys(:)
1486
  LOGICAL :: lk, lt, li
1487
  INTEGER :: iq, nq
1488
1489
  !--- DETERMINE THE DATABASE TO BE USED (ky, tracers or isotope)
1490
  lk = PRESENT(ky)
1491
  lt = .NOT.lk .AND. ALLOCATED(tracers);  IF(lt) lt = SIZE(tracers)  /= 0; IF(lt) lt = ANY(tracers(1)%keys%key(:) == keyn)
1492
  li = .NOT.lt .AND. ALLOCATED(isotopes); IF(li) li = SIZE(isotopes) /= 0; IF(li) li = ANY(isotope%keys(1)%key(:) == keyn)
1493
1494
  !--- LINK "keys" TO THE RIGHT DATABASE
1495
  IF(test(.NOT.ANY([lk,lt,li]), lerr)) RETURN
1496
  IF(lk) keys => ky(:)
1497
  IF(lt) keys => tracers(:)%keys
1498
  IF(li) keys => isotope%keys(:)
1499
1500
  !--- GET THE DATA
1501
  nq = SIZE(tname)
1502
  ALLOCATE(val(nq))
1503
  lerr = ANY([(getKeyByName_s1(keyn, val(iq), tname(iq), keys(:)), iq=1, nq)])
1504
  IF(PRESENT(nam)) nam = tname(:)
1505
1506
END FUNCTION getKeyByName_sm
1507
!==============================================================================================================================
1508

7
LOGICAL FUNCTION getKey_sm(keyn, val, ky, nam) RESULT(lerr)
1509
  CHARACTER(LEN=*),                             INTENT(IN)  :: keyn
1510
  CHARACTER(LEN=maxlen),           ALLOCATABLE, INTENT(OUT) :: val(:)
1511
  TYPE(keys_type),       OPTIONAL,              INTENT(IN)  :: ky(:)
1512
  CHARACTER(LEN=maxlen), OPTIONAL, ALLOCATABLE, INTENT(OUT) :: nam(:)
1513
!------------------------------------------------------------------------------------------------------------------------------
1514
! Note: if names are repeated, getKeyByName_sm can't be used ; this routine, using indexes, must be used instead.
1515

7
  IF(PRESENT(ky)) THEN                                                                   !=== KEY FROM "ky"
1516



49
    val = fgetKeys(keyn, ky, lerr=lerr)
1517



31
    IF(PRESENT(nam)) nam = ky(:)%name
1518
  ELSE
1519
    IF(         .NOT.test(.NOT.ALLOCATED(tracers ), lerr)) lerr = SIZE(tracers ) == 0    !=== KEY FROM "tracers"
1520
    IF(.NOT.lerr) val = fgetKeys(keyn, tracers%keys, lerr=lerr)
1521
    IF(.NOT.lerr.AND.PRESENT(nam)) nam = tracers(:)%keys%name
1522
    IF(lerr.AND..NOT.test(.NOT.ALLOCATED(isotopes), lerr)) lerr = SIZE(isotopes) == 0    !=== KEY FROM "isotope"
1523
    IF(.NOT.lerr) val = fgetKeys(keyn, isotope%keys, lerr=lerr)
1524
    IF(.NOT.lerr.AND.PRESENT(nam)) nam = isotope%keys(:)%name
1525
  END IF
1526
7
END FUNCTION getKey_sm
1527
!==============================================================================================================================
1528
LOGICAL FUNCTION getKeyByName_i1(keyn, val, tname, ky) RESULT(lerr)
1529
  CHARACTER(LEN=*),          INTENT(IN)  :: keyn
1530
  INTEGER,                   INTENT(OUT) :: val
1531
  CHARACTER(LEN=*),          INTENT(IN)  :: tname
1532
  TYPE(keys_type), OPTIONAL, INTENT(IN)  :: ky(:)
1533
!------------------------------------------------------------------------------------------------------------------------------
1534
  CHARACTER(LEN=maxlen) :: sval
1535
  INTEGER :: ierr
1536
  lerr = getKeyByName_s1(keyn, sval, tname, ky)
1537
  IF(test(fmsg('key "'//TRIM(keyn)//'" or tracer "'//TRIM(tname)//'" is missing', modname, lerr), lerr)) RETURN
1538
  READ(sval, *, IOSTAT=ierr) val
1539
  IF(test(fmsg('key "'//TRIM(keyn)//'" of tracer "'//TRIM(tname)//'" is not an integer', modname, ierr/=0), lerr)) RETURN
1540
END FUNCTION getKeyByName_i1
1541
!==============================================================================================================================
1542
LOGICAL FUNCTION getKeyByName_i1m(keyn, val, tname, ky) RESULT(lerr)
1543
  CHARACTER(LEN=*),          INTENT(IN)  :: keyn
1544
  INTEGER,      ALLOCATABLE, INTENT(OUT) :: val(:)
1545
  CHARACTER(LEN=*),          INTENT(IN)  :: tname
1546
  TYPE(keys_type), OPTIONAL, INTENT(IN)  ::  ky(:)
1547
!------------------------------------------------------------------------------------------------------------------------------
1548
  CHARACTER(LEN=maxlen), ALLOCATABLE :: sval(:)
1549
  INTEGER :: ierr, iq, nq
1550
  IF(test(getKeyByName_s1m(keyn, sval, tname, ky), lerr)) RETURN
1551
  nq = SIZE(sval); ALLOCATE(val(nq))
1552
  lerr = .FALSE.; DO iq=1, nq; READ(sval(iq), *, IOSTAT=ierr) val(iq); lerr = lerr .OR. ierr /= 0; END DO
1553
  IF(test(fmsg('key "'//TRIM(keyn)//'" of tracer "'//TRIM(tname)//'" is not an integer', modname, lerr), lerr)) RETURN
1554
END FUNCTION getKeyByName_i1m
1555
!==============================================================================================================================
1556
LOGICAL FUNCTION getKeyByName_im(keyn, val, tname, ky, nam) RESULT(lerr)
1557
  CHARACTER(LEN=*),                             INTENT(IN)  ::  keyn
1558
  INTEGER,                         ALLOCATABLE, INTENT(OUT) ::   val(:)
1559
  CHARACTER(LEN=*),                             INTENT(IN)  :: tname(:)
1560
  TYPE(keys_type),       OPTIONAL,              INTENT(IN)  ::    ky(:)
1561
  CHARACTER(LEN=maxlen), OPTIONAL, ALLOCATABLE, INTENT(OUT) ::   nam(:)
1562
!------------------------------------------------------------------------------------------------------------------------------
1563
  CHARACTER(LEN=maxlen), ALLOCATABLE :: sval(:), names(:)
1564
  INTEGER :: ierr, iq, nq
1565
  IF(test(getKeyByName_sm(keyn, sval, tname, ky, names), lerr)) RETURN
1566
  nq = SIZE(sval); ALLOCATE(val(nq))
1567
  DO iq = 1, nq                                                      !--- CONVERT THE KEYS TO INTEGERS
1568
    READ(sval(iq), *, IOSTAT=ierr) val(iq)
1569
    IF(test(fmsg('key "'//TRIM(keyn)//'" of tracer "'//TRIM(names(iq))//'" is not an integer', modname, ierr/=0), lerr)) RETURN
1570
  END DO
1571
  IF(PRESENT(nam)) nam = names(:)
1572
END FUNCTION getKeyByName_im
1573
!==============================================================================================================================
1574

4
LOGICAL FUNCTION getKey_im(keyn, val, ky, nam) RESULT(lerr)
1575
  CHARACTER(LEN=*),                             INTENT(IN)  :: keyn
1576
  INTEGER,                         ALLOCATABLE, INTENT(OUT) ::  val(:)
1577
  TYPE(keys_type),       OPTIONAL,              INTENT(IN)  ::   ky(:)
1578
  CHARACTER(LEN=maxlen), OPTIONAL, ALLOCATABLE, INTENT(OUT) ::  nam(:)
1579
!------------------------------------------------------------------------------------------------------------------------------
1580
4
  CHARACTER(LEN=maxlen), ALLOCATABLE :: sval(:), names(:)
1581
  INTEGER :: ierr, iq, nq
1582

4
  IF(test(getKey_sm(keyn, sval, ky, names), lerr)) RETURN
1583

4
  nq = SIZE(sval); ALLOCATE(val(nq))
1584
24
  DO iq = 1, nq
1585
20
    READ(sval(iq), *, IOSTAT=ierr) val(iq)
1586

24
    IF(test(fmsg('key "'//TRIM(keyn)//'" of tracer "'//TRIM(names(iq))//'" is not an integer', modname, ierr/=0), lerr)) RETURN
1587
  END DO
1588




4
  IF(PRESENT(nam)) nam = names
1589

4
END FUNCTION getKey_im
1590
!==============================================================================================================================
1591
LOGICAL FUNCTION getKeyByName_r1(keyn, val, tname, ky) RESULT(lerr)
1592
  CHARACTER(LEN=*),          INTENT(IN)  :: keyn
1593
  REAL,                      INTENT(OUT) :: val
1594
  CHARACTER(LEN=*),          INTENT(IN)  :: tname
1595
  TYPE(keys_type), OPTIONAL, INTENT(IN)  :: ky(:)
1596
!------------------------------------------------------------------------------------------------------------------------------
1597
  CHARACTER(LEN=maxlen) :: sval
1598
  INTEGER :: ierr
1599
  lerr = getKeyByName_s1(keyn, sval, tname, ky)
1600
  IF(test(fmsg('key "'//TRIM(keyn)//'" or tracer "'//TRIM(tname)//'" is missing',    modname, lerr), lerr)) RETURN
1601
  READ(sval, *, IOSTAT=ierr) val
1602
  IF(test(fmsg('key "'//TRIM(keyn)//'" of tracer "'//TRIM(tname)//'" is not a real', modname, ierr/=0), lerr)) RETURN
1603
END FUNCTION getKeyByName_r1
1604
!==============================================================================================================================
1605
LOGICAL FUNCTION getKeyByName_r1m(keyn, val, tname, ky) RESULT(lerr)
1606
  CHARACTER(LEN=*),           INTENT(IN)  :: keyn
1607
  REAL,          ALLOCATABLE, INTENT(OUT) :: val(:)
1608
  CHARACTER(LEN=*),           INTENT(IN)  :: tname
1609
  TYPE(keys_type),  OPTIONAL, INTENT(IN)  ::  ky(:)
1610
!------------------------------------------------------------------------------------------------------------------------------
1611
  CHARACTER(LEN=maxlen), ALLOCATABLE :: sval(:)
1612
  INTEGER :: ierr, iq, nq
1613
  IF(test(getKeyByName_s1m(keyn, sval, tname, ky), lerr)) RETURN
1614
  nq = SIZE(sval); ALLOCATE(val(nq))
1615
  lerr = .FALSE.; DO iq=1, nq; READ(sval(iq), *, IOSTAT=ierr) val(iq); lerr = lerr .OR. ierr /= 0; END DO
1616
  IF(test(fmsg('key "'//TRIM(keyn)//'" of tracer "'//TRIM(tname)//'" is not a vector of reals', modname, lerr), lerr)) RETURN
1617
END FUNCTION getKeyByName_r1m
1618
!==============================================================================================================================
1619
LOGICAL FUNCTION getKeyByName_rm(keyn, val, tname, ky, nam) RESULT(lerr)
1620
  CHARACTER(LEN=*),                             INTENT(IN)  ::  keyn
1621
  REAL,                            ALLOCATABLE, INTENT(OUT) ::   val(:)
1622
  CHARACTER(LEN=*),                             INTENT(IN)  :: tname(:)
1623
  TYPE(keys_type),       OPTIONAL,              INTENT(IN)  ::    ky(:)
1624
  CHARACTER(LEN=maxlen), OPTIONAL, ALLOCATABLE, INTENT(OUT) ::   nam(:)
1625
!------------------------------------------------------------------------------------------------------------------------------
1626
  CHARACTER(LEN=maxlen), ALLOCATABLE :: sval(:), names(:)
1627
  INTEGER :: ierr, iq, nq
1628
  IF(test(getKeyByName_sm(keyn, sval, tname, ky, names), lerr)) RETURN
1629
  nq = SIZE(sval); ALLOCATE(val(nq))
1630
  DO iq = 1, nq                                                      !--- CONVERT THE KEYS TO INTEGERS
1631
    READ(sval(iq), *, IOSTAT=ierr) val(iq)
1632
    IF(test(fmsg('key "'//TRIM(keyn)//'" of tracer "'//TRIM(names(iq))//'" is not a real', modname, ierr/=0), lerr)) RETURN
1633
  END DO
1634
  IF(PRESENT(nam)) nam = names
1635
END FUNCTION getKeyByName_rm
1636
!==============================================================================================================================
1637
LOGICAL FUNCTION getKey_rm(keyn, val, ky, nam) RESULT(lerr)
1638
  CHARACTER(LEN=*),                             INTENT(IN)  :: keyn
1639
  REAL,                            ALLOCATABLE, INTENT(OUT) ::  val(:)
1640
  TYPE(keys_type),       OPTIONAL,              INTENT(IN)  ::   ky(:)
1641
  CHARACTER(LEN=maxlen), OPTIONAL, ALLOCATABLE, INTENT(OUT) ::  nam(:)
1642
!------------------------------------------------------------------------------------------------------------------------------
1643
  CHARACTER(LEN=maxlen), ALLOCATABLE :: sval(:), names(:)
1644
  INTEGER :: ierr, iq, nq
1645
  IF(test(getKey_sm(keyn, sval, ky, names), lerr)) RETURN
1646
  nq = SIZE(sval); ALLOCATE(val(nq))
1647
  DO iq = 1, nq                                                      !--- CONVERT THE KEYS TO INTEGERS
1648
    READ(sval(iq), *, IOSTAT=ierr) val(iq)
1649
    IF(test(fmsg('key "'//TRIM(keyn)//'" of tracer "'//TRIM(names(iq))//'" is not a real', modname, ierr/=0), lerr)) RETURN
1650
  END DO
1651
  IF(PRESENT(nam)) nam = names
1652
END FUNCTION getKey_rm
1653
!==============================================================================================================================
1654
LOGICAL FUNCTION getKeyByName_l1(keyn, val, tname, ky) RESULT(lerr)
1655
  USE strings_mod, ONLY: str2bool
1656
  CHARACTER(LEN=*),          INTENT(IN)  :: keyn
1657
  LOGICAL,                   INTENT(OUT) :: val
1658
  CHARACTER(LEN=*),          INTENT(IN)  :: tname
1659
  TYPE(keys_type), OPTIONAL, INTENT(IN)  :: ky(:)
1660
!------------------------------------------------------------------------------------------------------------------------------
1661
  CHARACTER(LEN=maxlen) :: sval
1662
  lerr = getKeyByName_s1(keyn, sval, tname, ky)
1663
  IF(test(fmsg('key "'//TRIM(keyn)//'" or tracer "'//TRIM(tname)//'" is missing', modname, lerr), lerr)) RETURN
1664
  val = str2bool(sval)
1665
END FUNCTION getKeyByName_l1
1666
!==============================================================================================================================
1667
LOGICAL FUNCTION getKeyByName_l1m(keyn, val, tname, ky) RESULT(lerr)
1668
  USE strings_mod, ONLY: str2bool
1669
  CHARACTER(LEN=*),           INTENT(IN)  :: keyn
1670
  LOGICAL,       ALLOCATABLE, INTENT(OUT) :: val(:)
1671
  CHARACTER(LEN=*),           INTENT(IN)  :: tname
1672
  TYPE(keys_type),  OPTIONAL, INTENT(IN)  ::  ky(:)
1673
!------------------------------------------------------------------------------------------------------------------------------
1674
  CHARACTER(LEN=maxlen), ALLOCATABLE :: sval(:)
1675
  INTEGER :: iq, nq
1676
  IF(test(getKeyByName_s1m(keyn, sval, tname, ky), lerr)) RETURN
1677
  nq = SIZE(sval); ALLOCATE(val(nq))
1678
  lerr = .FALSE.; DO iq=1, nq; val(iq)=str2bool(sval(iq)); END DO
1679
END FUNCTION getKeyByName_l1m
1680
!==============================================================================================================================
1681
LOGICAL FUNCTION getKeyByName_lm(keyn, val, tname, ky, nam) RESULT(lerr)
1682
  USE strings_mod, ONLY: str2bool
1683
  CHARACTER(LEN=*),                             INTENT(IN)  ::  keyn
1684
  LOGICAL,                         ALLOCATABLE, INTENT(OUT) ::   val(:)
1685
  CHARACTER(LEN=*),                             INTENT(IN)  :: tname(:)
1686
  TYPE(keys_type),       OPTIONAL,              INTENT(IN)  ::    ky(:)
1687
  CHARACTER(LEN=maxlen), OPTIONAL, ALLOCATABLE, INTENT(OUT) ::   nam(:)
1688
!------------------------------------------------------------------------------------------------------------------------------
1689
  CHARACTER(LEN=maxlen), ALLOCATABLE :: sval(:)
1690
  INTEGER :: iq, nq
1691
  IF(test(getKeyByName_sm(keyn, sval, tname, ky, nam), lerr)) RETURN
1692
  nq = SIZE(sval); ALLOCATE(val(nq))
1693
  lerr = .FALSE.; DO iq=1, nq; val(iq)=str2bool(sval(iq)); END DO
1694
END FUNCTION getKeyByName_lm
1695
!==============================================================================================================================
1696
LOGICAL FUNCTION getKey_lm(keyn, val, ky, nam) RESULT(lerr)
1697
  USE strings_mod, ONLY: str2bool
1698
  CHARACTER(LEN=*),                             INTENT(IN)  :: keyn
1699
  LOGICAL,                         ALLOCATABLE, INTENT(OUT) ::  val(:)
1700
  TYPE(keys_type),       OPTIONAL,              INTENT(IN)  ::   ky(:)
1701
  CHARACTER(LEN=maxlen), OPTIONAL, ALLOCATABLE, INTENT(OUT) ::  nam(:)
1702
!------------------------------------------------------------------------------------------------------------------------------
1703
  CHARACTER(LEN=maxlen), ALLOCATABLE :: sval(:)
1704
  INTEGER :: iq, nq
1705
  IF(test(getKey_sm(keyn, sval, ky, nam), lerr)) RETURN
1706
  nq = SIZE(sval); ALLOCATE(val(nq))
1707
  lerr = .FALSE.; DO iq=1, nq; val(iq)=str2bool(sval(iq)); END DO
1708
END FUNCTION getKey_lm
1709
!==============================================================================================================================
1710
1711
1712
!==============================================================================================================================
1713
!=== ROUTINES TO GET OR PUT TRACERS AND ISOTOPES DATABASES, SINCE tracers AND isotopes ARE PRIVATE VARIABLES ==================
1714
!==============================================================================================================================
1715
SUBROUTINE setKeysDBase(tracers_, isotopes_, isotope_)
1716
  TYPE(trac_type), OPTIONAL, INTENT(IN) ::  tracers_(:)
1717
  TYPE(isot_type), OPTIONAL, INTENT(IN) :: isotopes_(:)
1718
  TYPE(isot_type), OPTIONAL, INTENT(IN) :: isotope_
1719
!------------------------------------------------------------------------------------------------------------------------------
1720
  TYPE(isot_type), ALLOCATABLE :: iso(:)
1721
  INTEGER :: ix, nbIso
1722
  IF(PRESENT( tracers_)) THEN;  tracers =  tracers_; ELSE; ALLOCATE( tracers(0)); END IF
1723
  IF(PRESENT(isotopes_)) THEN; isotopes = isotopes_; ELSE; ALLOCATE(isotopes(0)); END IF
1724
  IF(PRESENT(isotope_ )) THEN
1725
    ix = strIdx(isotopes(:)%parent, isotope_%parent)
1726
    IF(ix /= 0) THEN
1727
      isotopes(ix) = isotope_
1728
    ELSE
1729
      nbIso = SIZE(isotopes); ALLOCATE(iso(nbIso+1)); iso(1:nbIso) = isotopes; iso(nbIso+1) = isotope_
1730
      CALL MOVE_ALLOC(FROM=iso, TO=isotopes)
1731
    END IF
1732
  END IF
1733
END SUBROUTINE setKeysDBase
1734
!==============================================================================================================================
1735
SUBROUTINE getKeysDBase(tracers_, isotopes_, isotope_)
1736
  TYPE(trac_type), OPTIONAL, ALLOCATABLE, INTENT(OUT) ::  tracers_(:)
1737
  TYPE(isot_type), OPTIONAL, ALLOCATABLE, INTENT(OUT) :: isotopes_(:)
1738
  TYPE(isot_type), OPTIONAL,              INTENT(OUT) :: isotope_
1739
!------------------------------------------------------------------------------------------------------------------------------
1740
  INTEGER :: ix
1741
  IF(PRESENT( tracers_)) THEN;  tracers_ =  tracers; ELSE; ALLOCATE( tracers_(0)); END IF
1742
  IF(PRESENT(isotopes_)) THEN; isotopes_ = isotopes; ELSE; ALLOCATE(isotopes_(0)); END IF
1743
  IF(PRESENT(isotope_ )) THEN; ix = strIdx(isotopes(:)%parent, isotope%parent); IF(ix /= 0) isotope_=isotopes(ix); END IF
1744
END SUBROUTINE getKeysDBase
1745
!==============================================================================================================================
1746
1747
1748
!==============================================================================================================================
1749
!=== REMOVE, IF ANY, OR ADD THE PHASE SUFFIX OF THE TRACER NAMED "s" ==========================================================
1750
!==============================================================================================================================
1751
50
ELEMENTAL CHARACTER(LEN=maxlen) FUNCTION delPhase(s) RESULT(out)
1752
  CHARACTER(LEN=*), INTENT(IN) :: s
1753
!------------------------------------------------------------------------------------------------------------------------------
1754
  INTEGER :: ix, ip, ns
1755
50
  out = s; ns = LEN_TRIM(s)
1756

50
  IF(s == '' .OR. ns<=2) RETURN                                                !--- Empty string or LEN(name)<=2: nothing to do
1757


32
  IF(s(1:3)=='H2O' .AND. INDEX(old_phases,s(4:4))/=0 .AND. (ns==4 .OR. s(5:5)=='_')) THEN
1758
    out='H2O'//s(5:ns)                                                         !--- H2O<phase>[_<iso>][_<tag>]
1759

32
  ELSE IF(s(ns-1:ns-1)==phases_sep .AND. INDEX(known_phases,s(ns:ns))/=0) THEN
1760
12
    out = s(1:ns-2); RETURN                                                    !--- <var><phase_sep><phase>
1761

120
  ELSE; DO ip = 1, nphases; ix = INDEX(s, phases_sep//known_phases(ip:ip)//'_'); IF(ix /= 0) EXIT; END DO
1762

20
    IF(ip /= nphases+1) out = s(1:ix-1)//s(ix+2:ns)                            !--- <var><phase_sep><phase>_<tag>
1763
  END IF
1764
50
END FUNCTION delPhase
1765
!==============================================================================================================================
1766
23
CHARACTER(LEN=maxlen) FUNCTION addPhase_s1(s,pha) RESULT(out)
1767
  CHARACTER(LEN=*),           INTENT(IN) :: s
1768
  CHARACTER(LEN=1),           INTENT(IN) :: pha
1769
!------------------------------------------------------------------------------------------------------------------------------
1770
  INTEGER :: l, i
1771
23
  out = s
1772
23
  IF(s == '') RETURN                                                           !--- Empty string: nothing to do
1773
23
  i = INDEX(s, '_')                                                            !--- /=0 for <var>_<tag> tracers names
1774
23
  l = LEN_TRIM(s)
1775

23
  IF(i == 0) out =  TRIM(s)//phases_sep//pha                                   !--- <var>       => return <var><sep><pha>
1776

23
  IF(i /= 0) out = s(1:i-1)//phases_sep//pha//'_'//s(i+1:l)                    !--- <var>_<tag> => return <var><sep><pha>_<tag>
1777
23
END FUNCTION addPhase_s1
1778
!==============================================================================================================================
1779
FUNCTION addPhase_sm(s,pha) RESULT(out)
1780
  CHARACTER(LEN=*),           INTENT(IN) :: s(:)
1781
  CHARACTER(LEN=1),           INTENT(IN) :: pha
1782
  CHARACTER(LEN=maxlen),     ALLOCATABLE :: out(:)
1783
!------------------------------------------------------------------------------------------------------------------------------
1784
  INTEGER :: k
1785
  out = [( addPhase_s1(s(k), pha), k=1, SIZE(s) )]
1786
END FUNCTION addPhase_sm
1787
!==============================================================================================================================
1788
8
CHARACTER(LEN=maxlen) FUNCTION addPhase_i1(s,ipha,phases) RESULT(out)
1789
  CHARACTER(LEN=*),           INTENT(IN) :: s
1790
  INTEGER,                    INTENT(IN) :: ipha
1791
  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: phases
1792
!------------------------------------------------------------------------------------------------------------------------------
1793
8
  out = s
1794
8
  IF(s == '') RETURN                                                           !--- Empty string: nothing to do
1795
8
  IF(ipha == 0 .OR. ipha > nphases) RETURN                                     !--- Absurd index: no phase to add
1796
8
  IF(     PRESENT(phases)) out = addPhase_s1(s,       phases(ipha:ipha))
1797
8
  IF(.NOT.PRESENT(phases)) out = addPhase_s1(s, known_phases(ipha:ipha))
1798
8
END FUNCTION addPhase_i1
1799
!==============================================================================================================================
1800
FUNCTION addPhase_im(s,ipha,phases) RESULT(out)
1801
  CHARACTER(LEN=*),           INTENT(IN) :: s(:)
1802
  INTEGER,                    INTENT(IN) :: ipha
1803
  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: phases
1804
  CHARACTER(LEN=maxlen),     ALLOCATABLE :: out(:)
1805
!------------------------------------------------------------------------------------------------------------------------------
1806
  INTEGER :: k
1807
  IF(     PRESENT(phases)) out = [( addPhase_i1(s(k), ipha,       phases), k=1, SIZE(s) )]
1808
  IF(.NOT.PRESENT(phases)) out = [( addPhase_i1(s(k), ipha, known_phases), k=1, SIZE(s) )]
1809
END FUNCTION addPhase_im
1810
!==============================================================================================================================
1811
1812
1813
!==============================================================================================================================
1814
!=== GET PHASE INDEX IN THE POSSIBLE PHASES LIST OR IN A SPECIFIED LIST ("phases") ============================================
1815
!==============================================================================================================================
1816
INTEGER FUNCTION getiPhase(tname, phases) RESULT(iPhase)
1817
  CHARACTER(LEN=*),           INTENT(IN)  :: tname
1818
  CHARACTER(LEN=*), OPTIONAL, INTENT(IN)  :: phases
1819
!------------------------------------------------------------------------------------------------------------------------------
1820
  CHARACTER(LEN=maxlen) :: phase
1821
  IF(     PRESENT(phases)) phase = getPhase(tname,       phases, iPhase)
1822
7
  IF(.NOT.PRESENT(phases)) phase = getPhase(tname, known_phases, iPhase)
1823
7
END FUNCTION getiPhase
1824
!==============================================================================================================================
1825
7
CHARACTER(LEN=1) FUNCTION getPhase(tname, phases, iPhase) RESULT(phase)
1826
  CHARACTER(LEN=*),           INTENT(IN)  :: tname
1827
  CHARACTER(LEN=*), OPTIONAL, INTENT(IN)  :: phases
1828
  INTEGER,          OPTIONAL, INTENT(OUT) :: iPhase
1829
!------------------------------------------------------------------------------------------------------------------------------
1830
  INTEGER :: ip
1831
7
  phase = TRIM(strHead(strTail(tname, phases_sep), '_', .TRUE.))     !--- <nam><sep><pha>[_<tag>] -> <pha>[_<tag>] -> <pha>
1832
7
  IF(     PRESENT(phases)) ip = INDEX(      phases, phase)
1833
7
  IF(.NOT.PRESENT(phases)) ip = INDEX(known_phases, phase)
1834
7
  IF(ip == 0) phase = 'g'
1835
7
  IF(PRESENT(iPhase)) iPhase = ip
1836
7
END FUNCTION getPhase
1837
!==============================================================================================================================
1838
1839
1840
!==============================================================================================================================
1841
!============ CONVERT WATER-DERIVED NAMES FROM FORMER TO CURRENT CONVENTION ; OTHER NAMES ARE LEFT UNTOUCHED ==================
1842
!======= NAMES STRUCTURE: H2O[<phase>][_<isotope>][_<tag>] (<phase> from "old_phases", <isotope> from "oldH2OIso") ============
1843
!==============================================================================================================================
1844
CHARACTER(LEN=maxlen) FUNCTION old2newH2O_1(oldName, iPhase) RESULT(newName)
1845
  CHARACTER(LEN=*),  INTENT(IN)  :: oldName
1846
  INTEGER, OPTIONAL, INTENT(OUT) :: iPhase
1847
!------------------------------------------------------------------------------------------------------------------------------
1848
  CHARACTER(LEN=maxlen), ALLOCATABLE :: tmp(:)
1849
  INTEGER :: ix, ip, nt
1850
  LOGICAL :: lerr
1851
  newName = oldName
1852
  IF(PRESENT(iPhase)) iPhase = 1                                               !--- Default: gaseous phase
1853
  lerr = strParse(oldName, '_', tmp, nt)                                       !--- Parsing: 1 up to 3 elements.
1854
  ip = strIdx( [('H2O'//old_phases(ix:ix), ix=1, nphases)], tmp(1) )           !--- Phase index
1855
  IF(ip /= 0 .AND. PRESENT(iPhase)) iPhase = ip                                !--- Returning phase index
1856
  IF(ip == 0 .AND. tmp(1) /= 'H2O')   RETURN                                   !--- Not an old-style water-related species
1857
  IF(nt == 1) THEN
1858
    newName = addPhase('H2O',ip)                                               !=== WATER WITH OR WITHOUT PHASE
1859
  ELSE
1860
    ix = strIdx(oldH2OIso, tmp(2))                                             !--- Index in the known isotopes list
1861
    IF(ix /= 0) newName = newH2OIso(ix)                                        !--- Move to new isotope name
1862
    IF(ip /= 0) newName = addPhase(newName, ip)                                !--- Add phase to isotope name
1863
    IF(nt == 3) newName = TRIM(newName)//'_'//TRIM(tmp(3))                     !=== WATER ISOTOPE OR TAGGING TRACER
1864
  END IF
1865
END FUNCTION old2newH2O_1
1866
!==============================================================================================================================
1867
FUNCTION old2newH2O_m(oldName) RESULT(newName)
1868
  CHARACTER(LEN=*), INTENT(IN) :: oldName(:)
1869
  CHARACTER(LEN=maxlen)        :: newName(SIZE(oldName))
1870
!------------------------------------------------------------------------------------------------------------------------------
1871
  INTEGER :: i
1872
  newName = [(old2newH2O_1(oldName(i)), i=1, SIZE(oldName))]
1873
END FUNCTION old2newH2O_m
1874
!==============================================================================================================================
1875
1876
1877
!==============================================================================================================================
1878
!============ CONVERT WATER-DERIVED NAMES FROM CURRENT TO FORMER CONVENTION ; OTHER NAMES ARE LEFT UNTOUCHED ==================
1879
!==== NAMES STRUCTURE: <var>[<phase_sep><phase>][_<tag>] (<phase> from "known_phases", <var> = 'H2O' or from "newH2OIso") =====
1880
!==============================================================================================================================
1881
7
CHARACTER(LEN=maxlen) FUNCTION new2oldH2O_1(newName, iPhase) RESULT(oldName)
1882
  CHARACTER(LEN=*),  INTENT(IN)  :: newName
1883
  INTEGER, OPTIONAL, INTENT(OUT) :: iPhase
1884
!------------------------------------------------------------------------------------------------------------------------------
1885
  INTEGER :: ix, ip
1886
  CHARACTER(LEN=maxlen) :: var
1887
7
  oldName = newName
1888
7
  ip = getiPhase(newName)                                                      !--- Phase index
1889
7
  IF(PRESENT(iPhase)) iPhase = MAX(ip, 1)                                      !--- Return phase index ; default: 1 (gazeous)
1890
7
  var = TRIM(strHead(newName, phases_sep, .TRUE.))                             !--- Variable without phase and tag
1891
7
  ix = strIdx(newH2OIso, var)                                                  !--- Index in the known H2O isotopes list
1892

7
  IF(ix == 0 .AND. var /= 'H2O') RETURN                                        !--- Neither H2O nor an H2O isotope => finished
1893
3
  oldName = 'H2O'
1894

3
  IF(ip /= 0) oldName = TRIM(oldName)//old_phases(ip:ip)                       !--- Add phase if needed
1895
3
  IF(ix /= 0) oldName = TRIM(oldName)//'_'//oldH2OIso(ix)                      !--- H2O isotope name
1896
3
  IF(newName /= addPhase(var, ip)) &
1897
3
    oldName = TRIM(oldName)//strTail(newName, '_', .TRUE.)                     !--- Add the tag suffix
1898

3
  IF(ip == 0 .AND. ix /= 0) oldName = strTail(oldName, '_')                    !--- Isotope with no phase: remove 'H2O_' prefix
1899
7
END FUNCTION new2oldH2O_1
1900
!==============================================================================================================================
1901
FUNCTION new2oldH2O_m(newName) RESULT(oldName)
1902
  CHARACTER(LEN=*), INTENT(IN) :: newName(:)
1903
  CHARACTER(LEN=maxlen)        :: oldName(SIZE(newName))
1904
!------------------------------------------------------------------------------------------------------------------------------
1905
  INTEGER :: i
1906
  oldName = [(new2oldH2O_1(newName(i)), i=1, SIZE(newName))]
1907
END FUNCTION new2oldH2O_m
1908
!==============================================================================================================================
1909
1910
1911
!==============================================================================================================================
1912
!=== GET THE NAME(S) OF THE ANCESTOR(S) OF TRACER(S) "tname" AT GENERATION "igen"  IN THE TRACERS DESCRIPTORS LIST "tr" =======
1913
!==============================================================================================================================
1914
SUBROUTINE ancestor_1(t, out, tname, igen)
1915
  TYPE(trac_type),       INTENT(IN)  :: t(:)
1916
  CHARACTER(LEN=maxlen), INTENT(OUT) :: out
1917
  CHARACTER(LEN=*),      INTENT(IN)  :: tname
1918
  INTEGER,     OPTIONAL, INTENT(IN)  :: igen
1919
!------------------------------------------------------------------------------------------------------------------------------
1920
  INTEGER :: ix
1921
  CALL idxAncestor_1(t, ix, tname, igen)
1922
  out = ''; IF(ix /= 0) out = t(ix)%name
1923
END SUBROUTINE ancestor_1
1924
!==============================================================================================================================
1925
SUBROUTINE ancestor_mt(t, out, tname, igen)
1926
  TYPE(trac_type),       INTENT(IN)  :: t(:)
1927
  CHARACTER(LEN=*),      INTENT(IN)  :: tname(:)
1928
  CHARACTER(LEN=maxlen), INTENT(OUT) :: out(SIZE(tname))
1929
  INTEGER,     OPTIONAL, INTENT(IN)  :: igen
1930
!------------------------------------------------------------------------------------------------------------------------------
1931
  INTEGER :: ix(SIZE(tname))
1932
  CALL idxAncestor_mt(t, ix, tname, igen)
1933
  out(:) = ''; WHERE(ix /= 0) out = t(ix)%name
1934
END SUBROUTINE ancestor_mt
1935
!==============================================================================================================================
1936
SUBROUTINE ancestor_m(t, out, igen)
1937
  TYPE(trac_type),       INTENT(IN)  :: t(:)
1938
  CHARACTER(LEN=maxlen), INTENT(OUT) :: out(SIZE(t))
1939
  INTEGER,     OPTIONAL, INTENT(IN)  :: igen
1940
!------------------------------------------------------------------------------------------------------------------------------
1941
  INTEGER :: ix(SIZE(t))
1942
  CALL idxAncestor_m(t, ix, igen)
1943
  out(:) = ''; WHERE(ix /= 0) out = t(ix)%name
1944
END SUBROUTINE ancestor_m
1945
!==============================================================================================================================
1946
1947
1948
!==============================================================================================================================
1949
!=== GET THE INDEX(ES) OF THE GENERATION "igen" ANCESTOR(S) OF "tname" (t%name IF UNSPECIFIED) IN THE "t" LIST ================
1950
!==============================================================================================================================
1951
50
SUBROUTINE idxAncestor_1(t, idx, tname, igen)
1952
  TYPE(trac_type),   INTENT(IN)  :: t(:)
1953
  INTEGER,           INTENT(OUT) :: idx
1954
  CHARACTER(LEN=*),  INTENT(IN)  :: tname
1955
  INTEGER, OPTIONAL, INTENT(IN)  :: igen
1956
  INTEGER :: ig
1957
50
  ig = 0; IF(PRESENT(igen)) ig = igen
1958

300
  idx = strIdx(t(:)%name, tname)
1959
50
  IF(idx == 0)                 RETURN            !--- Tracer not found
1960
50
  IF(t(idx)%iGeneration <= ig) RETURN            !--- Tracer has a lower generation number than asked generation 'igen"
1961
  DO WHILE(t(idx)%iGeneration > ig); idx = strIdx(t(:)%name, t(idx)%parent); END DO
1962
END SUBROUTINE idxAncestor_1
1963
!------------------------------------------------------------------------------------------------------------------------------
1964
SUBROUTINE idxAncestor_mt(t, idx, tname, igen)
1965
  TYPE(trac_type),   INTENT(IN)  :: t(:)
1966
  CHARACTER(LEN=*),  INTENT(IN)  :: tname(:)
1967
  INTEGER,           INTENT(OUT) :: idx(SIZE(tname))
1968
  INTEGER, OPTIONAL, INTENT(IN)  :: igen
1969
  INTEGER :: ix
1970
  DO ix = 1, SIZE(tname); CALL idxAncestor_1(t, idx(ix), tname(ix), igen); END DO
1971
END SUBROUTINE idxAncestor_mt
1972
!------------------------------------------------------------------------------------------------------------------------------
1973
10
SUBROUTINE idxAncestor_m(t, idx, igen)
1974
  TYPE(trac_type),   INTENT(IN)  :: t(:)
1975
  INTEGER,           INTENT(OUT) :: idx(SIZE(t))
1976
  INTEGER, OPTIONAL, INTENT(IN)  :: igen
1977
  INTEGER :: ix
1978
60
  DO ix = 1, SIZE(t); CALL idxAncestor_1(t, idx(ix), t(ix)%name, igen); END DO
1979
10
END SUBROUTINE idxAncestor_m
1980
!==============================================================================================================================
1981
1982
1983
END MODULE readTracFiles_mod