| 1 | MODULE strings_mod |
|---|
| 2 | |
|---|
| 3 | IMPLICIT NONE |
|---|
| 4 | |
|---|
| 5 | PRIVATE |
|---|
| 6 | PUBLIC :: maxlen, init_printout, msg, fmsg, get_in, lunout, prt_level |
|---|
| 7 | PUBLIC :: strLower, strHead, strStack, strCount, strReduce, strClean, strIdx |
|---|
| 8 | PUBLIC :: strUpper, strTail, strStackm, strParse, strReplace, strFind, find, cat |
|---|
| 9 | PUBLIC :: dispTable, dispOutliers, dispNameList |
|---|
| 10 | PUBLIC :: is_numeric, bool2str, int2str, real2str, dble2str |
|---|
| 11 | PUBLIC :: reduceExpr, str2bool, str2int, str2real, str2dble |
|---|
| 12 | PUBLIC :: addQuotes, testFile, checkList, removeComment, test |
|---|
| 13 | |
|---|
| 14 | INTERFACE get_in; MODULE PROCEDURE getin_s, getin_i, getin_r, getin_l; END INTERFACE get_in |
|---|
| 15 | INTERFACE msg; MODULE PROCEDURE msg_1, msg_m; END INTERFACE msg |
|---|
| 16 | INTERFACE fmsg; MODULE PROCEDURE fmsg_1, fmsg_m; END INTERFACE fmsg |
|---|
| 17 | INTERFACE strHead; MODULE PROCEDURE strHead_1, strHead_m; END INTERFACE strHead |
|---|
| 18 | INTERFACE strTail; MODULE PROCEDURE strTail_1, strTail_m; END INTERFACE strTail |
|---|
| 19 | INTERFACE strClean; MODULE PROCEDURE strClean_1, strClean_m; END INTERFACE strClean |
|---|
| 20 | INTERFACE strReduce; MODULE PROCEDURE strReduce_1, strReduce_2; END INTERFACE strReduce |
|---|
| 21 | INTERFACE strIdx; MODULE PROCEDURE strIdx_1, strIdx_m; END INTERFACE strIdx |
|---|
| 22 | INTERFACE strCount; MODULE PROCEDURE strCount_m1, strCount_11, strCount_1m; END INTERFACE strCount |
|---|
| 23 | INTERFACE strParse; MODULE PROCEDURE strParse_1, strParse_m; END INTERFACE strParse |
|---|
| 24 | INTERFACE strReplace; MODULE PROCEDURE strReplace_1, strReplace_m; END INTERFACE strReplace |
|---|
| 25 | INTERFACE cat; MODULE PROCEDURE horzcat_s1, horzcat_i1, horzcat_r1, & |
|---|
| 26 | ! horzcat_d1, horzcat_dm, |
|---|
| 27 | horzcat_sm, horzcat_im, horzcat_rm; END INTERFACE cat |
|---|
| 28 | INTERFACE find; MODULE PROCEDURE strFind, find_int, find_boo; END INTERFACE find |
|---|
| 29 | INTERFACE dispOutliers; MODULE PROCEDURE dispOutliers_1, dispOutliers_2; END INTERFACE dispOutliers |
|---|
| 30 | INTERFACE reduceExpr; MODULE PROCEDURE reduceExpr_1, reduceExpr_m; END INTERFACE reduceExpr |
|---|
| 31 | INTERFACE addQuotes; MODULE PROCEDURE addQuotes_1, addQuotes_m; END INTERFACE addQuotes |
|---|
| 32 | INTERFACE testFile; MODULE PROCEDURE testFile_1, testFile_m; END INTERFACE testFile |
|---|
| 33 | |
|---|
| 34 | INTEGER, PARAMETER :: maxlen = 256 !--- Standard maximum length for strings |
|---|
| 35 | INTEGER, SAVE :: lunout = 6 !--- Printing unit (default: 6, ie. on screen) |
|---|
| 36 | INTEGER, SAVE :: prt_level = 1 !--- Printing level (default: 1, ie. print all) |
|---|
| 37 | |
|---|
| 38 | CONTAINS |
|---|
| 39 | |
|---|
| 40 | LOGICAL FUNCTION test(lcond, lout) RESULT(lerr) |
|---|
| 41 | LOGICAL, INTENT(IN) :: lcond |
|---|
| 42 | LOGICAL, INTENT(OUT) :: lout |
|---|
| 43 | lerr = lcond; lout = lcond |
|---|
| 44 | END FUNCTION test |
|---|
| 45 | |
|---|
| 46 | !============================================================================================================================== |
|---|
| 47 | SUBROUTINE init_printout(lunout_, prt_level_) |
|---|
| 48 | INTEGER, INTENT(IN) :: lunout_, prt_level_ |
|---|
| 49 | lunout = lunout_ |
|---|
| 50 | END SUBROUTINE init_printout |
|---|
| 51 | !============================================================================================================================== |
|---|
| 52 | |
|---|
| 53 | |
|---|
| 54 | !============================================================================================================================== |
|---|
| 55 | !=== Same as getin ; additional last argument: the default value. |
|---|
| 56 | !============================================================================================================================== |
|---|
| 57 | SUBROUTINE getin_s(nam, val, def) |
|---|
| 58 | USE ioipsl_getincom, ONLY: getin |
|---|
| 59 | CHARACTER(LEN=*), INTENT(IN) :: nam |
|---|
| 60 | CHARACTER(LEN=*), INTENT(INOUT) :: val |
|---|
| 61 | CHARACTER(LEN=*), INTENT(IN) :: def |
|---|
| 62 | val = def; CALL getin(nam, val) |
|---|
| 63 | IF(val/=def) WRITE(lunout,*)TRIM(nam)//' = '//TRIM(val) |
|---|
| 64 | END SUBROUTINE getin_s |
|---|
| 65 | !============================================================================================================================== |
|---|
| 66 | SUBROUTINE getin_i(nam, val, def) |
|---|
| 67 | USE ioipsl_getincom, ONLY: getin |
|---|
| 68 | CHARACTER(LEN=*), INTENT(IN) :: nam |
|---|
| 69 | INTEGER, INTENT(INOUT) :: val |
|---|
| 70 | INTEGER, INTENT(IN) :: def |
|---|
| 71 | val = def; CALL getin(nam, val) |
|---|
| 72 | IF(val/=def) WRITE(lunout,*)TRIM(nam)//' = '//TRIM(int2str(val)) |
|---|
| 73 | END SUBROUTINE getin_i |
|---|
| 74 | !============================================================================================================================== |
|---|
| 75 | SUBROUTINE getin_r(nam, val, def) |
|---|
| 76 | USE ioipsl_getincom, ONLY: getin |
|---|
| 77 | CHARACTER(LEN=*), INTENT(IN) :: nam |
|---|
| 78 | REAL, INTENT(INOUT) :: val |
|---|
| 79 | REAL, INTENT(IN) :: def |
|---|
| 80 | val = def; CALL getin(nam, val) |
|---|
| 81 | IF(val/=def) WRITE(lunout,*)TRIM(nam)//' = '//TRIM(real2str(val)) |
|---|
| 82 | END SUBROUTINE getin_r |
|---|
| 83 | !============================================================================================================================== |
|---|
| 84 | SUBROUTINE getin_l(nam, val, def) |
|---|
| 85 | USE ioipsl_getincom, ONLY: getin |
|---|
| 86 | CHARACTER(LEN=*), INTENT(IN) :: nam |
|---|
| 87 | LOGICAL, INTENT(INOUT) :: val |
|---|
| 88 | LOGICAL, INTENT(IN) :: def |
|---|
| 89 | val = def; CALL getin(nam, val) |
|---|
| 90 | IF(val.NEQV.def) WRITE(lunout,*)TRIM(nam)//' = '//TRIM(bool2str(val)) |
|---|
| 91 | END SUBROUTINE getin_l |
|---|
| 92 | !============================================================================================================================== |
|---|
| 93 | |
|---|
| 94 | |
|---|
| 95 | !============================================================================================================================== |
|---|
| 96 | !=== Display one or several messages, one each line, starting with the current routine name "modname". |
|---|
| 97 | !============================================================================================================================== |
|---|
| 98 | SUBROUTINE msg_1(str, modname, ll, unit) |
|---|
| 99 | !--- Display a simple message "str". Optional parameters: |
|---|
| 100 | ! * "modname": module name, displayed in front of the message (with ": " separator) if present. |
|---|
| 101 | ! * "ll": message trigger ; message is displayed only if ll==.TRUE. |
|---|
| 102 | ! * "unit": write unit (by default: "lunout") |
|---|
| 103 | CHARACTER(LEN=*), INTENT(IN) :: str |
|---|
| 104 | CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: modname |
|---|
| 105 | LOGICAL, OPTIONAL, INTENT(IN) :: ll |
|---|
| 106 | INTEGER, OPTIONAL, INTENT(IN) :: unit |
|---|
| 107 | INTEGER :: unt |
|---|
| 108 | IF(PRESENT(ll)) THEN; IF(.NOT.ll) RETURN; END IF |
|---|
| 109 | unt = lunout; IF(PRESENT(unit)) unt = unit |
|---|
| 110 | IF(PRESENT(modname)) THEN |
|---|
| 111 | WRITE(unt,'(a)') TRIM(modname)//': '//str !--- Routine name provided |
|---|
| 112 | ELSE |
|---|
| 113 | WRITE(unt,'(a)') str !--- Simple message |
|---|
| 114 | END IF |
|---|
| 115 | END SUBROUTINE msg_1 |
|---|
| 116 | !============================================================================================================================== |
|---|
| 117 | SUBROUTINE msg_m(str, modname, ll, unit, nmax) |
|---|
| 118 | !--- Same as msg_1 with multiple strings that are stacked (separator: coma) on up to "nmax" full lines. |
|---|
| 119 | CHARACTER(LEN=*), INTENT(IN) :: str(:) |
|---|
| 120 | CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: modname |
|---|
| 121 | LOGICAL, OPTIONAL, INTENT(IN) :: ll |
|---|
| 122 | INTEGER, OPTIONAL, INTENT(IN) :: unit |
|---|
| 123 | INTEGER, OPTIONAL, INTENT(IN) :: nmax |
|---|
| 124 | CHARACTER(LEN=maxlen), ALLOCATABLE :: s(:) |
|---|
| 125 | INTEGER :: unt, nmx, k |
|---|
| 126 | LOGICAL :: l |
|---|
| 127 | l = .TRUE.; IF(PRESENT(ll)) l = ll |
|---|
| 128 | unt = lunout; IF(PRESENT(unit)) unt = unit |
|---|
| 129 | nmx = 128; IF(PRESENT(nmax)) nmx = nmax |
|---|
| 130 | s = strStackm(str, ', ', nmx) |
|---|
| 131 | IF(PRESENT(modname)) THEN |
|---|
| 132 | DO k=1,SIZE(s); CALL msg_1(s(k), modname, l, unt); END DO |
|---|
| 133 | ELSE |
|---|
| 134 | DO k=1,SIZE(s); CALL msg_1(s(k), ll=l, unit=unt); END DO |
|---|
| 135 | END IF |
|---|
| 136 | END SUBROUTINE msg_m |
|---|
| 137 | !============================================================================================================================== |
|---|
| 138 | LOGICAL FUNCTION fmsg_1(str, modname, ll, unit) RESULT(l) |
|---|
| 139 | CHARACTER(LEN=*), INTENT(IN) :: str |
|---|
| 140 | CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: modname |
|---|
| 141 | LOGICAL, OPTIONAL, INTENT(IN) :: ll |
|---|
| 142 | INTEGER, OPTIONAL, INTENT(IN) :: unit |
|---|
| 143 | INTEGER :: unt |
|---|
| 144 | l = .TRUE.; IF(PRESENT(ll)) l = ll |
|---|
| 145 | unt = lunout; IF(PRESENT(unit)) unt = unit |
|---|
| 146 | IF(PRESENT(modname)) THEN |
|---|
| 147 | CALL msg_1(str, modname, l, unt) |
|---|
| 148 | ELSE |
|---|
| 149 | CALL msg_1(str, ll=l, unit=unt) |
|---|
| 150 | END IF |
|---|
| 151 | END FUNCTION fmsg_1 |
|---|
| 152 | !============================================================================================================================== |
|---|
| 153 | LOGICAL FUNCTION fmsg_m(str, modname, ll, unit, nmax) RESULT(l) |
|---|
| 154 | CHARACTER(LEN=*), INTENT(IN) :: str(:) |
|---|
| 155 | CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: modname |
|---|
| 156 | LOGICAL, OPTIONAL, INTENT(IN) :: ll |
|---|
| 157 | INTEGER, OPTIONAL, INTENT(IN) :: unit |
|---|
| 158 | INTEGER, OPTIONAL, INTENT(IN) :: nmax |
|---|
| 159 | INTEGER :: unt, nmx |
|---|
| 160 | l = .TRUE.; IF(PRESENT(ll)) l = ll |
|---|
| 161 | unt = lunout; IF(PRESENT(unit)) unt = unit |
|---|
| 162 | nmx = 128; IF(PRESENT(nmax)) nmx = nmax |
|---|
| 163 | IF(PRESENT(modname)) THEN |
|---|
| 164 | CALL msg_m(str, modname, l, unt, nmx) |
|---|
| 165 | ELSE |
|---|
| 166 | CALL msg_m(str, ll=l, unit=unt, nmax=nmx) |
|---|
| 167 | END IF |
|---|
| 168 | END FUNCTION fmsg_m |
|---|
| 169 | !============================================================================================================================== |
|---|
| 170 | |
|---|
| 171 | |
|---|
| 172 | !============================================================================================================================== |
|---|
| 173 | !=== Lower/upper case conversion function. ==================================================================================== |
|---|
| 174 | !============================================================================================================================== |
|---|
| 175 | ELEMENTAL CHARACTER(LEN=maxlen) FUNCTION strLower(str) RESULT(out) |
|---|
| 176 | CHARACTER(LEN=*), INTENT(IN) :: str |
|---|
| 177 | INTEGER :: k |
|---|
| 178 | out = str |
|---|
| 179 | DO k=1,LEN_TRIM(str) |
|---|
| 180 | IF(str(k:k)>='A'.OR.str(k:k)<='Z') out(k:k)=ACHAR(IACHAR(str(k:k))+32) |
|---|
| 181 | END DO |
|---|
| 182 | END FUNCTION strLower |
|---|
| 183 | !============================================================================================================================== |
|---|
| 184 | ELEMENTAL CHARACTER(LEN=maxlen) FUNCTION strUpper(str) RESULT(out) |
|---|
| 185 | CHARACTER(LEN=*), INTENT(IN) :: str |
|---|
| 186 | INTEGER :: k |
|---|
| 187 | out = str |
|---|
| 188 | DO k=1,LEN_TRIM(str) |
|---|
| 189 | IF(str(k:k)>='a'.OR.str(k:k)<='z') out(k:k)=ACHAR(IACHAR(str(k:k))-32) |
|---|
| 190 | END DO |
|---|
| 191 | END FUNCTION strUpper |
|---|
| 192 | !============================================================================================================================== |
|---|
| 193 | |
|---|
| 194 | |
|---|
| 195 | !============================================================================================================================== |
|---|
| 196 | !=== Extract the substring in front of the last (first if lFirst==TRUE) occurrence of separator "sep" in "str" ================ |
|---|
| 197 | !=== Examples for str='a_b_c' and sep='_' and the bash command with the same effect: ================ |
|---|
| 198 | !=== * strHead(..,.FALSE.) = 'a' ${str%%$sep*} ================ |
|---|
| 199 | !=== * strHead(..,.TRUE.) = 'a_b' ${str%$sep*} ================ |
|---|
| 200 | !============================================================================================================================== |
|---|
| 201 | CHARACTER(LEN=maxlen) FUNCTION strHead_1(str,sep,lFirst) RESULT(out) |
|---|
| 202 | CHARACTER(LEN=*), INTENT(IN) :: str |
|---|
| 203 | CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: sep |
|---|
| 204 | LOGICAL, OPTIONAL, INTENT(IN) :: lFirst |
|---|
| 205 | LOGICAL :: lf |
|---|
| 206 | lf = .FALSE.; IF(PRESENT(lFirst)) lf=lFirst |
|---|
| 207 | IF(PRESENT(sep)) THEN |
|---|
| 208 | out = str(1:INDEX(str,sep,.NOT.lf)-1) |
|---|
| 209 | ELSE |
|---|
| 210 | out = str(1:INDEX(str,'/',.NOT.lf)-1) |
|---|
| 211 | END IF |
|---|
| 212 | IF(out == '') out = str |
|---|
| 213 | END FUNCTION strHead_1 |
|---|
| 214 | !============================================================================================================================== |
|---|
| 215 | FUNCTION strHead_m(str,sep,lFirst) RESULT(out) |
|---|
| 216 | CHARACTER(LEN=maxlen), ALLOCATABLE :: out(:) |
|---|
| 217 | CHARACTER(LEN=*), INTENT(IN) :: str(:) |
|---|
| 218 | CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: sep |
|---|
| 219 | LOGICAL, OPTIONAL, INTENT(IN) :: lFirst |
|---|
| 220 | LOGICAL :: lf |
|---|
| 221 | INTEGER :: k |
|---|
| 222 | lf = .FALSE.; IF(PRESENT(lFirst)) lf=lFirst |
|---|
| 223 | IF(PRESENT(sep)) THEN |
|---|
| 224 | out = [(strHead_1(str(k),sep,.NOT.lf), k=1, SIZE(str))] |
|---|
| 225 | ELSE |
|---|
| 226 | out = [(strHead_1(str(k),lFirst=.NOT.lf), k=1, SIZE(str))] |
|---|
| 227 | END IF |
|---|
| 228 | END FUNCTION strHead_m |
|---|
| 229 | !============================================================================================================================== |
|---|
| 230 | !=== Extract the substring following the last (first if lFirst==TRUE) occurrence of separator "sep" in "str" ================ |
|---|
| 231 | !=== Examples for str='a_b_c' and sep='_' and the bash command with the same effect: ================ |
|---|
| 232 | !=== * strHead(..,.FALSE.) = 'b_c' ${str#*$sep} ================ |
|---|
| 233 | !=== * strHead(..,.TRUE.) = 'c' ${str##*$sep} ================ |
|---|
| 234 | !============================================================================================================================== |
|---|
| 235 | CHARACTER(LEN=maxlen) FUNCTION strTail_1(str,sep,lFirst) RESULT(out) |
|---|
| 236 | CHARACTER(LEN=*), INTENT(IN) :: str |
|---|
| 237 | CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: sep |
|---|
| 238 | LOGICAL, OPTIONAL, INTENT(IN) :: lFirst |
|---|
| 239 | LOGICAL :: lf |
|---|
| 240 | lf = .FALSE.; IF(PRESENT(lFirst)) lf=lFirst |
|---|
| 241 | IF(PRESENT(sep)) THEN |
|---|
| 242 | out = str(INDEX(str,sep,.NOT.lf)+LEN(sep):LEN_TRIM(str)) |
|---|
| 243 | ELSE |
|---|
| 244 | out = str(INDEX(str,'/',.NOT.lf)+1:LEN_TRIM(str)) |
|---|
| 245 | END IF |
|---|
| 246 | IF(out == '') out = str |
|---|
| 247 | END FUNCTION strTail_1 |
|---|
| 248 | !============================================================================================================================== |
|---|
| 249 | FUNCTION strTail_m(str,sep,lFirst) RESULT(out) |
|---|
| 250 | CHARACTER(LEN=maxlen), ALLOCATABLE :: out(:) |
|---|
| 251 | CHARACTER(LEN=*), INTENT(IN) :: str(:) |
|---|
| 252 | CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: sep |
|---|
| 253 | LOGICAL, OPTIONAL, INTENT(IN) :: lFirst |
|---|
| 254 | LOGICAL :: lf |
|---|
| 255 | INTEGER :: k |
|---|
| 256 | lf = .FALSE.; IF(PRESENT(lFirst)) lf=lFirst |
|---|
| 257 | IF(PRESENT(sep)) THEN |
|---|
| 258 | out = [(strTail_1(str(k),sep,.NOT.lf), k=1, SIZE(str))] |
|---|
| 259 | ELSE |
|---|
| 260 | out = [(strTail_1(str(k),lFirst=.NOT.lf), k=1, SIZE(str))] |
|---|
| 261 | END IF |
|---|
| 262 | END FUNCTION strTail_m |
|---|
| 263 | !============================================================================================================================== |
|---|
| 264 | |
|---|
| 265 | |
|---|
| 266 | !============================================================================================================================== |
|---|
| 267 | !=== Concatenates the strings "str(:)" with separator "sep" into a single string using a separator (',' by default). ========== |
|---|
| 268 | !============================================================================================================================== |
|---|
| 269 | FUNCTION strStack(str, sep, mask) RESULT(out) |
|---|
| 270 | CHARACTER(LEN=:), ALLOCATABLE :: out |
|---|
| 271 | CHARACTER(LEN=*), INTENT(IN) :: str(:) |
|---|
| 272 | CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: sep |
|---|
| 273 | LOGICAL, OPTIONAL, INTENT(IN) :: mask(:) |
|---|
| 274 | CHARACTER(LEN=:), ALLOCATABLE :: s |
|---|
| 275 | INTEGER :: is, i0 |
|---|
| 276 | IF(SIZE(str) == 0) THEN; out = ''; RETURN; END IF |
|---|
| 277 | ALLOCATE(s, SOURCE=', '); IF(PRESENT(sep)) s=sep |
|---|
| 278 | IF(PRESENT(mask)) THEN |
|---|
| 279 | IF(ALL(.NOT.mask)) THEN; out = ''; RETURN; END IF |
|---|
| 280 | i0 = 0; DO WHILE(.NOT.mask(i0+1)); i0 = i0+1; END DO |
|---|
| 281 | out = str(i0); DO is=i0+1,SIZE(str, DIM=1); IF(.NOT.mask(is)) CYCLE; out = TRIM(out)//s//TRIM(str(is)); END DO |
|---|
| 282 | ELSE |
|---|
| 283 | out = str(1); DO is=2,SIZE(str, DIM=1); out = TRIM(out)//s//TRIM(str(is)); END DO |
|---|
| 284 | END IF |
|---|
| 285 | END FUNCTION strStack |
|---|
| 286 | !============================================================================================================================== |
|---|
| 287 | !=== Concatenate the strings "str(:)" with separator "sep" into one or several lines of "nmax" characters max (for display) === |
|---|
| 288 | !============================================================================================================================== |
|---|
| 289 | FUNCTION strStackm(str, sep, nmax) RESULT(out) |
|---|
| 290 | CHARACTER(LEN=maxlen), ALLOCATABLE :: out(:) |
|---|
| 291 | CHARACTER(LEN=*), INTENT(IN) :: str(:) |
|---|
| 292 | CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: sep |
|---|
| 293 | INTEGER, OPTIONAL, INTENT(IN) :: nmax |
|---|
| 294 | CHARACTER(LEN=maxlen), ALLOCATABLE :: t(:) |
|---|
| 295 | CHARACTER(LEN=maxlen) :: sp |
|---|
| 296 | INTEGER :: is, ns, no, mx, n |
|---|
| 297 | IF(SIZE(str) == 0) THEN; out = ['']; RETURN; END IF |
|---|
| 298 | sp =', '; IF(PRESENT(sep )) sp = sep |
|---|
| 299 | ns = 2 ; IF(PRESENT(sep )) ns = LEN(sep) |
|---|
| 300 | mx = 256; IF(PRESENT(nmax)) mx = nmax |
|---|
| 301 | no = 1; out = [''] |
|---|
| 302 | DO is = 1, SIZE(str) |
|---|
| 303 | n = LEN_TRIM(str(is)); IF(out(no)/='') n = n+ns+LEN_TRIM(out(no)) !--- Line length after "str(is)" inclusion |
|---|
| 304 | IF(out(no) == '') THEN |
|---|
| 305 | out(no) = str(is) !--- Empty new line: set to "str(is)" |
|---|
| 306 | ELSE IF(n <= mx) THEN |
|---|
| 307 | out(no) = TRIM(out(no))//sp(1:ns)//TRIM(str(is)) !--- Append "str(is)" to the current line |
|---|
| 308 | ELSE |
|---|
| 309 | ALLOCATE(t(no+1)); t(1:no) = out; no=no+1; t(no) = str(is) !--- Full line: "str(si)" put in next line |
|---|
| 310 | CALL MOVE_ALLOC(FROM=t, TO=out) |
|---|
| 311 | END IF |
|---|
| 312 | END DO |
|---|
| 313 | END FUNCTION strStackm |
|---|
| 314 | !============================================================================================================================== |
|---|
| 315 | |
|---|
| 316 | |
|---|
| 317 | !============================================================================================================================== |
|---|
| 318 | !=== String cleaning: replace tabulation by spaces, remove NULL characters and comments. ====================================== |
|---|
| 319 | !============================================================================================================================== |
|---|
| 320 | SUBROUTINE strClean_1(str) |
|---|
| 321 | CHARACTER(LEN=*), INTENT(INOUT) :: str |
|---|
| 322 | INTEGER :: k, n, m |
|---|
| 323 | n = LEN(str) |
|---|
| 324 | DO k = n, 1, -1 |
|---|
| 325 | m = IACHAR(str(k:k)) |
|---|
| 326 | IF(m==9) str(k:k) = ' ' !--- Replace the tabulations with spaces |
|---|
| 327 | IF(m==0) str(k:n) = str(k+1:n)//' ' !--- Remove the NULL characters |
|---|
| 328 | END DO |
|---|
| 329 | m = INDEX(str,'!')-1; IF(m==-1) m = LEN_TRIM(str) !--- Remove end of line comment |
|---|
| 330 | str = ADJUSTL(str(1:m)) |
|---|
| 331 | END SUBROUTINE strClean_1 |
|---|
| 332 | !============================================================================================================================== |
|---|
| 333 | SUBROUTINE strClean_m(str) |
|---|
| 334 | CHARACTER(LEN=*), INTENT(INOUT) :: str(:) |
|---|
| 335 | INTEGER :: k |
|---|
| 336 | DO k = 1, SIZE(str); CALL strClean_1(str(k)); END DO |
|---|
| 337 | END SUBROUTINE strClean_m |
|---|
| 338 | !============================================================================================================================== |
|---|
| 339 | |
|---|
| 340 | |
|---|
| 341 | !============================================================================================================================== |
|---|
| 342 | !=== strReduce_1(str1) : Remove duplicated elements of str1. =========================================================== |
|---|
| 343 | !=== strReduce_2(str1,str2): Append str1 with new elements of str2. =========================================================== |
|---|
| 344 | !============================================================================================================================== |
|---|
| 345 | SUBROUTINE strReduce_1(str, nb) |
|---|
| 346 | CHARACTER(LEN=*), ALLOCATABLE, INTENT(INOUT) :: str(:) |
|---|
| 347 | INTEGER, OPTIONAL, INTENT(OUT) :: nb |
|---|
| 348 | CHARACTER(LEN=maxlen), ALLOCATABLE :: s1(:) |
|---|
| 349 | INTEGER :: k, n, n1 |
|---|
| 350 | IF(PRESENT(nb)) nb = 0 |
|---|
| 351 | CALL MOVE_ALLOC(FROM=str, TO=s1); CALL strClean(s1) |
|---|
| 352 | n1 = SIZE(s1, DIM=1) !--- Total nb. of elements in "s1" |
|---|
| 353 | n = COUNT( [( ALL(s1(1:k-1)/=s1(k)), k=1, n1 )] ) !--- Nb of unique elements in "s1" |
|---|
| 354 | ALLOCATE(str(n)) |
|---|
| 355 | IF(n==0) RETURN |
|---|
| 356 | str(1) = s1(1) |
|---|
| 357 | n=1; DO k=1,n1; IF(ANY(s1(1:k-1)==s1(k))) CYCLE; n=n+1; str(n)=s1(k); END DO |
|---|
| 358 | IF(PRESENT(nb)) nb=n |
|---|
| 359 | END SUBROUTINE strReduce_1 |
|---|
| 360 | !============================================================================================================================== |
|---|
| 361 | SUBROUTINE strReduce_2(str1, str2) |
|---|
| 362 | CHARACTER(LEN=*), ALLOCATABLE, INTENT(INOUT) :: str1(:) |
|---|
| 363 | CHARACTER(LEN=*), INTENT(IN) :: str2(:) |
|---|
| 364 | CHARACTER(LEN=maxlen), ALLOCATABLE :: s1(:), s2(:) |
|---|
| 365 | INTEGER :: k |
|---|
| 366 | IF(SIZE(str2)==0) RETURN |
|---|
| 367 | s2 = str2; CALL strClean(s2) |
|---|
| 368 | IF(.NOT.ALLOCATED(s2)) RETURN |
|---|
| 369 | IF(SIZE(s2) == 0) THEN; DEALLOCATE(s2); RETURN; END IF |
|---|
| 370 | IF(.NOT.ALLOCATED(str1)) THEN |
|---|
| 371 | str1 = s2 |
|---|
| 372 | ELSE IF(SIZE(str1)==0) THEN |
|---|
| 373 | str1 = s2 |
|---|
| 374 | ELSE |
|---|
| 375 | s1 = str1; CALL strClean(s1) |
|---|
| 376 | str1 = [s1, PACK(s2, MASK= [( ALL(s1(:) /= s2(k)), k=1, SIZE(s2) )] ) ] |
|---|
| 377 | END IF |
|---|
| 378 | END SUBROUTINE strReduce_2 |
|---|
| 379 | !============================================================================================================================== |
|---|
| 380 | |
|---|
| 381 | |
|---|
| 382 | !============================================================================================================================== |
|---|
| 383 | !=== GET THE INDEX OF THE FIRST APPEARANCE IN THE STRING VECTOR "str(:)" OF THE STRING(s) "s" ================================= |
|---|
| 384 | !============================================================================================================================== |
|---|
| 385 | INTEGER FUNCTION strIdx_1(str, s) RESULT(out) |
|---|
| 386 | CHARACTER(LEN=*), INTENT(IN) :: str(:), s |
|---|
| 387 | DO out = 1, SIZE(str); IF(str(out) == s) EXIT; END DO |
|---|
| 388 | IF(out == 1+SIZE(str) .OR. SIZE(str)==0) out = 0 |
|---|
| 389 | END FUNCTION strIdx_1 |
|---|
| 390 | !============================================================================================================================== |
|---|
| 391 | FUNCTION strIdx_m(str, s, n) RESULT(out) |
|---|
| 392 | CHARACTER(LEN=*), INTENT(IN) :: str(:), s(:) |
|---|
| 393 | INTEGER, OPTIONAL, INTENT(OUT) :: n |
|---|
| 394 | INTEGER, ALLOCATABLE :: out(:) |
|---|
| 395 | INTEGER :: k |
|---|
| 396 | out = [(strIdx_1(str(:), s(k)), k=1, SIZE(s))] |
|---|
| 397 | IF(PRESENT(n)) n = COUNT(out(:)/=0) |
|---|
| 398 | END FUNCTION strIdx_m |
|---|
| 399 | !============================================================================================================================== |
|---|
| 400 | |
|---|
| 401 | |
|---|
| 402 | !============================================================================================================================== |
|---|
| 403 | !=== GET THE INDEX LIST OF THE ELEMENTS OF "str(:)" EQUAL TO "s" AND OPTIONALY, ITS LENGTH "n" ================================ |
|---|
| 404 | !============================================================================================================================== |
|---|
| 405 | FUNCTION strFind(str, s, n) RESULT(out) |
|---|
| 406 | CHARACTER(LEN=*), INTENT(IN) :: str(:), s |
|---|
| 407 | INTEGER, OPTIONAL, INTENT(OUT) :: n |
|---|
| 408 | INTEGER, ALLOCATABLE :: out(:) |
|---|
| 409 | INTEGER :: k |
|---|
| 410 | out = PACK( [(k, k=1, SIZE(str(:), DIM=1))], MASK = str(:) == s ) |
|---|
| 411 | IF(PRESENT(n)) n = SIZE(out(:), DIM=1) |
|---|
| 412 | END FUNCTION strFind |
|---|
| 413 | !============================================================================================================================== |
|---|
| 414 | FUNCTION find_int(i,j,n) RESULT(out) |
|---|
| 415 | INTEGER, INTENT(IN) :: i(:), j |
|---|
| 416 | INTEGER, OPTIONAL, INTENT(OUT) :: n |
|---|
| 417 | INTEGER, ALLOCATABLE :: out(:) |
|---|
| 418 | INTEGER :: k |
|---|
| 419 | out = PACK( [(k, k=1, SIZE(i(:), DIM=1))], MASK = i(:) == j ) |
|---|
| 420 | IF(PRESENT(n)) n = SIZE(out(:), DIM=1) |
|---|
| 421 | END FUNCTION find_int |
|---|
| 422 | !============================================================================================================================== |
|---|
| 423 | FUNCTION find_boo(l,n) RESULT(out) |
|---|
| 424 | LOGICAL, INTENT(IN) :: l(:) |
|---|
| 425 | INTEGER, OPTIONAL, INTENT(OUT) :: n |
|---|
| 426 | INTEGER, ALLOCATABLE :: out(:) |
|---|
| 427 | INTEGER :: k |
|---|
| 428 | out = PACK( [(k, k=1, SIZE(l(:), DIM=1))], MASK = l(:) ) |
|---|
| 429 | IF(PRESENT(n)) n = SIZE(out(:), DIM=1) |
|---|
| 430 | END FUNCTION find_boo |
|---|
| 431 | !============================================================================================================================== |
|---|
| 432 | |
|---|
| 433 | |
|---|
| 434 | |
|---|
| 435 | !============================================================================================================================== |
|---|
| 436 | !=== GET 1ST APPEARANCE INDEX OF EACH ELEMENT OF "t(:)" IN "s(:)" (UNFOUND: INDEX=0) ========================================== |
|---|
| 437 | !=== OPTIONALY: GET THE NUMBER OF FOUND ELEMENTS "n" ========================================== |
|---|
| 438 | !============================================================================================================================== |
|---|
| 439 | |
|---|
| 440 | |
|---|
| 441 | !============================================================================================================================== |
|---|
| 442 | !=== GET THE INDEX IN "rawList" OF THE 1ST APPEARANCE OF ONE OF THE "del(:)" SEPARATORS (0 IF NONE OF THEM ARE PRESENT) |
|---|
| 443 | !=== IF lSc == .TRUE.: * SKIP HEAD SIGNS OR EXPONENTS SIGNS THAT SHOULD NOT BE CONFUSED WITH SEPARATORS |
|---|
| 444 | !=== * THEN TEST WHETHER THE STRING FROM START TO THE FOUND SEPARATOR IS A CORRECTLY FORMATTED NUMBER |
|---|
| 445 | !============================================================================================================================== |
|---|
| 446 | LOGICAL FUNCTION strIdx_prv(rawList, del, ibeg, idx, idel, lSc) RESULT(lerr) |
|---|
| 447 | CHARACTER(LEN=*), INTENT(IN) :: rawList !--- String in which delimiters have to be identified |
|---|
| 448 | CHARACTER(LEN=*), INTENT(IN) :: del(:) !--- List of delimiters |
|---|
| 449 | INTEGER, INTENT(IN) :: ibeg !--- Start index |
|---|
| 450 | INTEGER, INTENT(OUT) :: idx !--- Index of the first identified delimiter in "rawList" |
|---|
| 451 | INTEGER, INTENT(OUT) :: idel !--- Index of the identified delimiter (0 if idx==0) |
|---|
| 452 | LOGICAL, OPTIONAL, INTENT(IN) :: lSc !--- Care about nbs with front sign or in scient. notation |
|---|
| 453 | |
|---|
| 454 | INTEGER :: idx0 !--- Used to display an identified non-numeric string |
|---|
| 455 | INTEGER, ALLOCATABLE :: ii(:) |
|---|
| 456 | LOGICAL :: ll, ls |
|---|
| 457 | CHARACTER(LEN=maxlen) :: d |
|---|
| 458 | ! modname = 'strIdx' |
|---|
| 459 | lerr = .FALSE. |
|---|
| 460 | idx = strIdx1(rawList, del, ibeg, idel) !--- del(idel) appears in "rawList" at position idx |
|---|
| 461 | IF(.NOT.PRESENT(lSc)) RETURN !--- No need to check exceptions for numbers => finished |
|---|
| 462 | IF(.NOT. lSc ) RETURN !--- No need to check exceptions for numbers => finished |
|---|
| 463 | IF(idx == 0) THEN !--- No element of "del" in "rawList": |
|---|
| 464 | lerr = .NOT.is_numeric(rawList(ibeg:)) !--- String must be a number |
|---|
| 465 | IF(lerr) idx = LEN_TRIM(rawList); RETURN !--- Update idx => rawList(ibeg:idx-1) is the whole string |
|---|
| 466 | END IF |
|---|
| 467 | idx0 = idx |
|---|
| 468 | IF(test(idx==1.AND.INDEX('+-',del(idel))/=0, lerr)) RETURN !--- Front separator different from +/-: error |
|---|
| 469 | IF(idx/=1.AND.is_numeric(rawList(ibeg:idx-1))) RETURN !--- The input string tail is a valid number |
|---|
| 470 | idx = strIdx1(rawList, del, idx+1, idel) !--- => TO THE NEXT DELIMITER |
|---|
| 471 | IF(idx == 0) THEN |
|---|
| 472 | lerr = .NOT.is_numeric(rawList(ibeg:)) !--- No delimiter detected: string must be a number |
|---|
| 473 | IF(lerr) idx = idx0; RETURN |
|---|
| 474 | END IF |
|---|
| 475 | idx0 = idx |
|---|
| 476 | IF(is_numeric(rawList(ibeg:idx-1))) RETURN !--- The input string tail is a valid number |
|---|
| 477 | IF(test( INDEX('eE',rawList(idx-1:idx-1)) /= 0 & !--- Sole possible exception: scientific notation: E+/- |
|---|
| 478 | .OR. INDEX('+-',del(idel)) /=0, lerr)) RETURN |
|---|
| 479 | idx = strIdx1(rawList, del, idx+1, idel) !--- => TO THE NEXT DELIMITER |
|---|
| 480 | IF(idx == 0) THEN |
|---|
| 481 | lerr = .NOT.is_numeric(rawList(ibeg:)) !--- No separator detected: string must be a number |
|---|
| 482 | IF(lerr) idx = idx0; RETURN |
|---|
| 483 | END IF |
|---|
| 484 | lerr = .NOT.is_numeric(rawList(ibeg:idx-1)) |
|---|
| 485 | CONTAINS |
|---|
| 486 | |
|---|
| 487 | INTEGER FUNCTION strIdx1(str, del, ib, id) RESULT(idx) |
|---|
| 488 | CHARACTER(LEN=*), INTENT(IN) :: str |
|---|
| 489 | CHARACTER(LEN=*), INTENT(IN) :: del(:) |
|---|
| 490 | INTEGER, INTENT(IN) :: ib |
|---|
| 491 | INTEGER, INTENT(OUT) :: id |
|---|
| 492 | |
|---|
| 493 | INTEGER :: nd, ns, i |
|---|
| 494 | INTEGER, ALLOCATABLE :: ii(:) |
|---|
| 495 | |
|---|
| 496 | nd = SIZE(del) !--- Number of separators |
|---|
| 497 | ns = LEN_TRIM(str) !--- Length of the raw chain |
|---|
| 498 | ii = [(INDEX( str(ib:ns), del(i) ), i = 1, nd)] !--- Determine the next separator start index |
|---|
| 499 | id = MINLOC( ii, MASK = ii /= 0, DIM = 1 ) !--- Current delimiter index in the "delimiter(:)" list |
|---|
| 500 | idx = 0 |
|---|
| 501 | IF(ANY(ii /= 0)) idx = MINVAL( ii, MASK = ii /= 0 ) + ib - 1 !--- Index in "str(1:ns)" of the delimiter first character |
|---|
| 502 | IF(idx == 0) id = 0 |
|---|
| 503 | END FUNCTION strIdx1 |
|---|
| 504 | |
|---|
| 505 | END FUNCTION strIdx_prv |
|---|
| 506 | !============================================================================================================================== |
|---|
| 507 | |
|---|
| 508 | |
|---|
| 509 | !============================================================================================================================== |
|---|
| 510 | !=== Return the index of first appearance of "del" in "str" starting from index "ib" |
|---|
| 511 | !============================================================================================================================== |
|---|
| 512 | INTEGER FUNCTION strIndex(str, del, ib) RESULT(idx) |
|---|
| 513 | CHARACTER(LEN=*), INTENT(IN) :: str |
|---|
| 514 | CHARACTER(LEN=*), INTENT(IN) :: del |
|---|
| 515 | INTEGER, INTENT(IN) :: ib |
|---|
| 516 | idx = INDEX( str(ib:LEN_TRIM(str)), del ) + ib -1 |
|---|
| 517 | END FUNCTION strIndex |
|---|
| 518 | !============================================================================================================================== |
|---|
| 519 | |
|---|
| 520 | |
|---|
| 521 | !============================================================================================================================== |
|---|
| 522 | !=== Count the number of elements separated by "delimiter" in list "rawList". ================================================= |
|---|
| 523 | !============================================================================================================================== |
|---|
| 524 | LOGICAL FUNCTION strCount_11(rawList, delimiter, nb, lSc) RESULT(out) |
|---|
| 525 | CHARACTER(LEN=*), INTENT(IN) :: rawList |
|---|
| 526 | CHARACTER(LEN=*), INTENT(IN) :: delimiter |
|---|
| 527 | INTEGER, INTENT(OUT) :: nb |
|---|
| 528 | LOGICAL, OPTIONAL, INTENT(IN) :: lSc |
|---|
| 529 | LOGICAL :: ll |
|---|
| 530 | ll = .FALSE.; IF(PRESENT(lSc)) ll = lSc |
|---|
| 531 | out = strCount_1m(rawList, [delimiter], nb, ll) |
|---|
| 532 | END FUNCTION strCount_11 |
|---|
| 533 | !============================================================================================================================== |
|---|
| 534 | LOGICAL FUNCTION strCount_m1(rawList, delimiter, nb, lSc) RESULT(out) |
|---|
| 535 | CHARACTER(LEN=*), INTENT(IN) :: rawList(:) |
|---|
| 536 | CHARACTER(LEN=*), INTENT(IN) :: delimiter |
|---|
| 537 | INTEGER, ALLOCATABLE, INTENT(OUT) :: nb(:) |
|---|
| 538 | LOGICAL, OPTIONAL, INTENT(IN) :: lSc |
|---|
| 539 | |
|---|
| 540 | LOGICAL :: ll |
|---|
| 541 | INTEGER :: id |
|---|
| 542 | |
|---|
| 543 | ll = .FALSE.; IF(PRESENT(lSc)) ll = lSc .AND. INDEX('+-', delimiter) /= 0 |
|---|
| 544 | out = .TRUE. |
|---|
| 545 | ALLOCATE(nb(SIZE(rawList))) |
|---|
| 546 | DO id = 1, SIZE(rawList) |
|---|
| 547 | out = out .AND. strCount_1m(rawList(id), [delimiter], nb(id), ll) |
|---|
| 548 | END DO |
|---|
| 549 | END FUNCTION strCount_m1 |
|---|
| 550 | !============================================================================================================================== |
|---|
| 551 | LOGICAL FUNCTION strCount_1m(rawList, delimiter, nb, lSc) RESULT(lerr) |
|---|
| 552 | CHARACTER(LEN=*), INTENT(IN) :: rawList |
|---|
| 553 | CHARACTER(LEN=*), INTENT(IN) :: delimiter(:) |
|---|
| 554 | INTEGER, INTENT(OUT) :: nb |
|---|
| 555 | LOGICAL, OPTIONAL, INTENT(IN) :: lSc |
|---|
| 556 | INTEGER :: ib, ie, jd, nr |
|---|
| 557 | LOGICAL :: ll |
|---|
| 558 | CHARACTER(LEN=1024) :: r |
|---|
| 559 | ! modname = 'strCount' |
|---|
| 560 | lerr = .FALSE. |
|---|
| 561 | ll = .FALSE.; IF(PRESENT(lSc)) ll = lSc |
|---|
| 562 | r = TRIM(ADJUSTL(rawList)) |
|---|
| 563 | nr = LEN_TRIM(r); IF(nr == 0) RETURN |
|---|
| 564 | nb = 1; ib = 1 |
|---|
| 565 | DO |
|---|
| 566 | lerr = strIdx_prv(r, delimiter, ib, ie, jd, ll) |
|---|
| 567 | IF(fmsg('"'//TRIM(r(ib:ie-1))//'" is not numeric', ll=lerr)) RETURN |
|---|
| 568 | IF(jd == 0) EXIT |
|---|
| 569 | ib = ie + LEN(delimiter(jd)) |
|---|
| 570 | DO WHILE(r(ib:ib) == ' ' .AND. ib < nr); ib = ib + 1; END DO !--- Skip spaces before next chain |
|---|
| 571 | nb = nb + 1 |
|---|
| 572 | END DO |
|---|
| 573 | END FUNCTION strCount_1m |
|---|
| 574 | !============================================================================================================================== |
|---|
| 575 | |
|---|
| 576 | |
|---|
| 577 | !============================================================================================================================== |
|---|
| 578 | !=== Purpose: Parse "delimiter"-separated list "rawList" into the pair keys(:), vals(:). ==================================== |
|---|
| 579 | !=== Corresponding "vals" remains empty if the element does not contain "=" sign. ==================================== |
|---|
| 580 | !============================================================================================================================== |
|---|
| 581 | LOGICAL FUNCTION strParse_1(rawList, delimiter, keys, lSc, vals, n) RESULT(lerr) |
|---|
| 582 | CHARACTER(LEN=*), INTENT(IN) :: rawList, delimiter |
|---|
| 583 | CHARACTER(LEN=maxlen), ALLOCATABLE, INTENT(OUT) :: keys(:) |
|---|
| 584 | LOGICAL, OPTIONAL, INTENT(IN) :: lSc !--- Take care about numbers in scientific notation |
|---|
| 585 | CHARACTER(LEN=maxlen), ALLOCATABLE, OPTIONAL, INTENT(OUT) :: vals(:) |
|---|
| 586 | INTEGER, OPTIONAL, INTENT(OUT) :: n |
|---|
| 587 | LOGICAL :: ll |
|---|
| 588 | ! modname = 'strParse' |
|---|
| 589 | ll = .FALSE.; IF(PRESENT(lSc)) ll = lSc |
|---|
| 590 | IF(.NOT.PRESENT(vals)) lerr = strParse_m(rawList, [delimiter], keys, ll) |
|---|
| 591 | IF( PRESENT(vals)) lerr = strParse_m(rawList, [delimiter], keys, ll, vals) |
|---|
| 592 | IF(PRESENT(n)) n = SIZE(keys) |
|---|
| 593 | END FUNCTION strParse_1 |
|---|
| 594 | !============================================================================================================================== |
|---|
| 595 | LOGICAL FUNCTION strParse_m(rawList, delimiter, keys, lSc, vals, n, id) RESULT(lerr) |
|---|
| 596 | CHARACTER(LEN=*), INTENT(IN) :: rawList, delimiter(:) |
|---|
| 597 | CHARACTER(LEN=maxlen), ALLOCATABLE, INTENT(OUT) :: keys(:) !--- Parsed keys vector |
|---|
| 598 | LOGICAL, OPTIONAL, INTENT(IN) :: lSc !--- Take care about numbers in scientific notation |
|---|
| 599 | CHARACTER(LEN=maxlen), OPTIONAL, ALLOCATABLE, INTENT(OUT) :: vals(:) !--- Values for <name>=<value> keys |
|---|
| 600 | INTEGER, OPTIONAL, INTENT(OUT) :: n !--- Length of the parsed vector |
|---|
| 601 | INTEGER, OPTIONAL, ALLOCATABLE, INTENT(OUT) :: id(:) !--- Indexes of the separators in "delimiter(:)" vector |
|---|
| 602 | |
|---|
| 603 | CHARACTER(LEN=1024) :: r |
|---|
| 604 | INTEGER :: nr, ik, nk, ib, ie, jd |
|---|
| 605 | LOGICAL :: ll |
|---|
| 606 | |
|---|
| 607 | ! modname = 'strParse' |
|---|
| 608 | ll = .FALSE.; IF(PRESENT(lSc)) ll = lSc |
|---|
| 609 | IF(test(fmsg("Couldn't parse list: non-numerical strings were found", ll=strCount_1m(rawList, delimiter, nk, ll)),lerr)) RETURN |
|---|
| 610 | |
|---|
| 611 | !--- FEW ALLOCATIONS |
|---|
| 612 | ALLOCATE(keys(nk)) |
|---|
| 613 | IF(PRESENT(vals)) ALLOCATE(vals(nk)) |
|---|
| 614 | IF(PRESENT(id)) ALLOCATE(id(nk-1)) |
|---|
| 615 | IF(PRESENT(n)) n = nk |
|---|
| 616 | |
|---|
| 617 | !--- PARSING |
|---|
| 618 | r = TRIM(ADJUSTL(rawList)) |
|---|
| 619 | nr = LEN_TRIM(r); IF(nr == 0) RETURN |
|---|
| 620 | ib = 1 |
|---|
| 621 | DO ik = 1, nk-1 |
|---|
| 622 | IF(test(fmsg('Non-numeric values found', ll=strIdx_prv(r, delimiter, ib, ie, jd, ll)),lerr)) RETURN |
|---|
| 623 | keys(ik) = r(ib:ie-1) |
|---|
| 624 | IF(PRESENT(vals)) CALL parseKeys(keys(ik), vals(ik)) !--- Parse a <key>=<val> pair |
|---|
| 625 | IF(PRESENT(id )) id(ik) = jd !--- Index in "delimiter(:)" of the "ik"th delimiter |
|---|
| 626 | ib = ie + LEN_TRIM( delimiter(jd) ) !--- Length of the current delimiter |
|---|
| 627 | DO WHILE(r(ib:ib) == ' ' .AND. ib < nr); ib = ib + 1; END DO !--- Skip spaces before next chain |
|---|
| 628 | END DO |
|---|
| 629 | keys(nk) = r(ib:nr) |
|---|
| 630 | IF(PRESENT(vals)) CALL parseKeys(keys(nk), vals(nk)) !--- Parse a <key>=<val> pair |
|---|
| 631 | |
|---|
| 632 | CONTAINS |
|---|
| 633 | |
|---|
| 634 | SUBROUTINE parseKeys(key, val) |
|---|
| 635 | CHARACTER(LEN=*), INTENT(INOUT) :: key |
|---|
| 636 | CHARACTER(LEN=*), INTENT(OUT) :: val |
|---|
| 637 | INTEGER :: ix |
|---|
| 638 | ix = INDEX(key, '='); IF(ix == 0) RETURN !--- First "=" index in "key" |
|---|
| 639 | val = ADJUSTL(key(ix+1:LEN_TRIM(key))) |
|---|
| 640 | key = ADJUSTL(key(1:ix-1)) |
|---|
| 641 | END SUBROUTINE parseKeys |
|---|
| 642 | |
|---|
| 643 | END FUNCTION strParse_m |
|---|
| 644 | !============================================================================================================================== |
|---|
| 645 | |
|---|
| 646 | |
|---|
| 647 | !============================================================================================================================== |
|---|
| 648 | !=== String substitution: replace "key" by "val" each time it appears in "str". |
|---|
| 649 | !============================================================================================================================== |
|---|
| 650 | SUBROUTINE strReplace_1(str, key, val, lsurr) |
|---|
| 651 | CHARACTER(LEN=*), INTENT(INOUT) :: str !--- Main string |
|---|
| 652 | CHARACTER(LEN=*), INTENT(IN) :: key, val !--- "key" will be replaced by "val" |
|---|
| 653 | LOGICAL, OPTIONAL, INTENT(IN) :: lsurr !--- TRUE => key must be surrounded by special characters to be substituted |
|---|
| 654 | |
|---|
| 655 | CHARACTER(LEN=1024) :: s, t |
|---|
| 656 | INTEGER :: i0, ix, nk, ns |
|---|
| 657 | LOGICAL :: lsur, lb, le |
|---|
| 658 | |
|---|
| 659 | lsur = .FALSE.; IF(PRESENT(lsurr)) lsur = lsurr |
|---|
| 660 | nk = LEN_TRIM(key) |
|---|
| 661 | i0 = 1 |
|---|
| 662 | DO |
|---|
| 663 | ns = LEN_TRIM(str) |
|---|
| 664 | ix = INDEX(str(i0:ns), TRIM(key)) !--- First appearance index of "key" in "s", starting from index "i0" |
|---|
| 665 | IF(ix == 0) EXIT |
|---|
| 666 | ix = ix + i0 -1 |
|---|
| 667 | IF(lsur) THEN !--- Key must be surrounded by special characters |
|---|
| 668 | !--- lb=.TRUE.: key is at the very beginning of "str" or located after a special character |
|---|
| 669 | lb = ix ==1; IF(.NOT.lb) lb = INDEX('+-*/()^', str(ix-1 :ix-1 ))/=0 |
|---|
| 670 | !--- le=.TRUE.: key is at the very end of "str" or located before a special character |
|---|
| 671 | le = ix+nk-1==ns; IF(.NOT.le) le = INDEX('+-*/()^', str(ix+nk:ix+nk))/=0 |
|---|
| 672 | IF(.NOT.(lb.AND.le)) THEN; i0 = i0 + nk; CYCLE; END IF |
|---|
| 673 | END IF |
|---|
| 674 | str = str(1:ix-1)//TRIM(val)//str(ix+nk:ns) |
|---|
| 675 | END DO |
|---|
| 676 | END SUBROUTINE strReplace_1 |
|---|
| 677 | !============================================================================================================================== |
|---|
| 678 | SUBROUTINE strReplace_m(str, key, val, lsurr) |
|---|
| 679 | CHARACTER(LEN=*), INTENT(INOUT) :: str(:) !--- Main strings vector |
|---|
| 680 | CHARACTER(LEN=*), INTENT(IN) :: key, val !--- "key" will be replaced by "val" |
|---|
| 681 | LOGICAL, OPTIONAL, INTENT(IN) :: lsurr !--- TRUE => key must be surrounded by special characters to be substituted |
|---|
| 682 | INTEGER :: k |
|---|
| 683 | LOGICAL :: ll |
|---|
| 684 | ll=.FALSE.; IF(PRESENT(lsurr)) ll=lsurr |
|---|
| 685 | DO k=1, SIZE(str); CALL strReplace_1(str(k),key,val,ll); END DO |
|---|
| 686 | END SUBROUTINE strReplace_m |
|---|
| 687 | !============================================================================================================================== |
|---|
| 688 | |
|---|
| 689 | |
|---|
| 690 | !============================================================================================================================== |
|---|
| 691 | !=== Contatenate horizontally scalars/vectors of strings/integers/reals into a vector/array =================================== |
|---|
| 692 | !============================================================================================================================== |
|---|
| 693 | FUNCTION horzcat_s1(s0, s1, s2, s3, s4, s5, s6, s7, s8, s9) RESULT(out) |
|---|
| 694 | CHARACTER(LEN=*), TARGET, INTENT(IN) :: s0 |
|---|
| 695 | CHARACTER(LEN=*), OPTIONAL, TARGET, INTENT(IN) :: s1, s2, s3, s4, s5, s6, s7, s8, s9 |
|---|
| 696 | CHARACTER(LEN=maxlen), ALLOCATABLE :: out(:) |
|---|
| 697 | CHARACTER(LEN=maxlen), POINTER :: s |
|---|
| 698 | LOGICAL :: lv(10) |
|---|
| 699 | INTEGER :: iv |
|---|
| 700 | lv = [ .TRUE. , PRESENT(s1), PRESENT(s2), PRESENT(s3), PRESENT(s4) , & |
|---|
| 701 | PRESENT(s5), PRESENT(s6), PRESENT(s7), PRESENT(s8), PRESENT(s9) ] |
|---|
| 702 | ALLOCATE(out(COUNT(lv))) |
|---|
| 703 | DO iv=1, COUNT(lv) |
|---|
| 704 | SELECT CASE(iv) |
|---|
| 705 | CASE(1); s=> s0; CASE(2); s=> s1; CASE(3); s=> s2; CASE(4); s=> s3; CASE(5); s=> s4 |
|---|
| 706 | CASE(6); s=> s5; CASE(7); s=> s6; CASE(8); s=> s7; CASE(9); s=> s8; CASE(10);s=> s9 |
|---|
| 707 | END SELECT |
|---|
| 708 | out(iv) = s |
|---|
| 709 | END DO |
|---|
| 710 | END FUNCTION horzcat_s1 |
|---|
| 711 | !============================================================================================================================== |
|---|
| 712 | FUNCTION horzcat_sm(s0, s1, s2, s3, s4, s5, s6, s7, s8, s9) RESULT(out) |
|---|
| 713 | CHARACTER(LEN=*), TARGET, DIMENSION(:), INTENT(IN) :: s0 |
|---|
| 714 | CHARACTER(LEN=*), OPTIONAL, TARGET, DIMENSION(:), INTENT(IN) :: s1, s2, s3, s4, s5, s6, s7, s8, s9 |
|---|
| 715 | CHARACTER(LEN=maxlen), ALLOCATABLE :: out(:,:) |
|---|
| 716 | CHARACTER(LEN=maxlen), POINTER :: s(:) |
|---|
| 717 | LOGICAL :: lv(10) |
|---|
| 718 | INTEGER :: nrow, ncol, iv, n |
|---|
| 719 | lv = [ .TRUE. , PRESENT(s1), PRESENT(s2), PRESENT(s3), PRESENT(s4) , & |
|---|
| 720 | PRESENT(s5), PRESENT(s6), PRESENT(s7), PRESENT(s8), PRESENT(s9) ] |
|---|
| 721 | nrow = SIZE(s0); ncol=COUNT(lv) |
|---|
| 722 | ALLOCATE(out(nrow, ncol)) |
|---|
| 723 | DO iv=1, ncol |
|---|
| 724 | SELECT CASE(iv) |
|---|
| 725 | CASE(1); s=> s0; CASE(2); s=> s1; CASE(3); s=> s2; CASE(4); s=> s3; CASE(5); s=> s4 |
|---|
| 726 | CASE(6); s=> s5; CASE(7); s=> s6; CASE(8); s=> s7; CASE(9); s=> s8; CASE(10);s=> s9 |
|---|
| 727 | END SELECT |
|---|
| 728 | n = SIZE(s, DIM=1) |
|---|
| 729 | IF(n/=nrow) THEN; CALL msg("Can't concatenate vectors of differing lengths"); STOP; END IF |
|---|
| 730 | out(:,iv) = s(:) |
|---|
| 731 | END DO |
|---|
| 732 | END FUNCTION horzcat_sm |
|---|
| 733 | !============================================================================================================================== |
|---|
| 734 | FUNCTION horzcat_i1(i0, i1, i2, i3, i4, i5, i6, i7, i8, i9) RESULT(out) |
|---|
| 735 | INTEGER, TARGET, INTENT(IN) :: i0 |
|---|
| 736 | INTEGER, OPTIONAL, TARGET, INTENT(IN) :: i1, i2, i3, i4, i5, i6, i7, i8, i9 |
|---|
| 737 | INTEGER, ALLOCATABLE :: out(:) |
|---|
| 738 | INTEGER, POINTER :: i |
|---|
| 739 | LOGICAL :: lv(10) |
|---|
| 740 | INTEGER :: iv |
|---|
| 741 | lv = [ .TRUE. , PRESENT(i1), PRESENT(i2), PRESENT(i3), PRESENT(i4) , & |
|---|
| 742 | PRESENT(i5), PRESENT(i6), PRESENT(i7), PRESENT(i8), PRESENT(i9) ] |
|---|
| 743 | ALLOCATE(out(COUNT(lv))) |
|---|
| 744 | DO iv=1, COUNT(lv) |
|---|
| 745 | SELECT CASE(iv) |
|---|
| 746 | CASE(1); i=> i0; CASE(2); i=> i1; CASE(3); i=> i2; CASE(4); i=> i3; CASE(5); i=> i4 |
|---|
| 747 | CASE(6); i=> i5; CASE(7); i=> i6; CASE(8); i=> i7; CASE(9); i=> i8; CASE(10);i=> i9 |
|---|
| 748 | END SELECT |
|---|
| 749 | out(iv) = i |
|---|
| 750 | END DO |
|---|
| 751 | END FUNCTION horzcat_i1 |
|---|
| 752 | !============================================================================================================================== |
|---|
| 753 | FUNCTION horzcat_im(i0, i1, i2, i3, i4, i5, i6, i7, i8, i9) RESULT(out) |
|---|
| 754 | INTEGER, TARGET, DIMENSION(:), INTENT(IN) :: i0 |
|---|
| 755 | INTEGER, OPTIONAL, TARGET, DIMENSION(:), INTENT(IN) :: i1, i2, i3, i4, i5, i6, i7, i8, i9 |
|---|
| 756 | INTEGER, ALLOCATABLE :: out(:,:) |
|---|
| 757 | INTEGER, POINTER :: i(:) |
|---|
| 758 | LOGICAL :: lv(10) |
|---|
| 759 | INTEGER :: nrow, ncol, iv, n |
|---|
| 760 | lv = [ .TRUE. , PRESENT(i1), PRESENT(i2), PRESENT(i3), PRESENT(i4) , & |
|---|
| 761 | PRESENT(i5), PRESENT(i6), PRESENT(i7), PRESENT(i8), PRESENT(i9) ] |
|---|
| 762 | nrow = SIZE(i0); ncol=COUNT(lv) |
|---|
| 763 | ALLOCATE(out(nrow, ncol)) |
|---|
| 764 | DO iv=1, ncol |
|---|
| 765 | SELECT CASE(iv) |
|---|
| 766 | CASE(1); i=> i0; CASE(2); i=> i1; CASE(3); i=> i2; CASE(4); i=> i3; CASE(5); i=> i4 |
|---|
| 767 | CASE(6); i=> i5; CASE(7); i=> i6; CASE(8); i=> i7; CASE(9); i=> i8; CASE(10);i=> i9 |
|---|
| 768 | END SELECT |
|---|
| 769 | n = SIZE(i, DIM=1) |
|---|
| 770 | IF(n/=nrow) THEN; CALL msg("Can't concatenate vectors of differing lengths"); STOP; END IF |
|---|
| 771 | out(:,iv) = i(:) |
|---|
| 772 | END DO |
|---|
| 773 | END FUNCTION horzcat_im |
|---|
| 774 | !============================================================================================================================== |
|---|
| 775 | FUNCTION horzcat_r1(r0, r1, r2, r3, r4, r5, r6, r7, r8, r9) RESULT(out) |
|---|
| 776 | REAL, TARGET, INTENT(IN) :: r0 |
|---|
| 777 | REAL, OPTIONAL, TARGET, INTENT(IN) :: r1, r2, r3, r4, r5, r6, r7, r8, r9 |
|---|
| 778 | REAL, ALLOCATABLE :: out(:) |
|---|
| 779 | REAL, POINTER :: r |
|---|
| 780 | LOGICAL :: lv(10) |
|---|
| 781 | INTEGER :: iv |
|---|
| 782 | lv = [ .TRUE. , PRESENT(r1), PRESENT(r2), PRESENT(r3), PRESENT(r4) , & |
|---|
| 783 | PRESENT(r5), PRESENT(r6), PRESENT(r7), PRESENT(r8), PRESENT(r9) ] |
|---|
| 784 | ALLOCATE(out(COUNT(lv))) |
|---|
| 785 | DO iv=1, COUNT(lv) |
|---|
| 786 | SELECT CASE(iv) |
|---|
| 787 | CASE(1); r=> r0; CASE(2); r=> r1; CASE(3); r=> r2; CASE(4); r=> r3; CASE(5); r=> r4 |
|---|
| 788 | CASE(6); r=> r5; CASE(7); r=> r6; CASE(8); r=> r7; CASE(9); r=> r8; CASE(10);r=> r9 |
|---|
| 789 | END SELECT |
|---|
| 790 | out(iv) = r |
|---|
| 791 | END DO |
|---|
| 792 | END FUNCTION horzcat_r1 |
|---|
| 793 | !============================================================================================================================== |
|---|
| 794 | FUNCTION horzcat_rm(r0, r1, r2, r3, r4, r5, r6, r7, r8, r9) RESULT(out) |
|---|
| 795 | REAL, TARGET, DIMENSION(:), INTENT(IN) :: r0 |
|---|
| 796 | REAL, OPTIONAL, TARGET, DIMENSION(:), INTENT(IN) :: r1, r2, r3, r4, r5, r6, r7, r8, r9 |
|---|
| 797 | REAL, ALLOCATABLE :: out(:,:) |
|---|
| 798 | REAL, POINTER :: r(:) |
|---|
| 799 | LOGICAL :: lv(10) |
|---|
| 800 | INTEGER :: nrow, ncol, iv, n |
|---|
| 801 | lv = [ .TRUE. , PRESENT(r1), PRESENT(r2), PRESENT(r3), PRESENT(r4) , & |
|---|
| 802 | PRESENT(r5), PRESENT(r6), PRESENT(r7), PRESENT(r8), PRESENT(r9) ] |
|---|
| 803 | nrow = SIZE(r0); ncol=COUNT(lv) |
|---|
| 804 | ALLOCATE(out(nrow, ncol)) |
|---|
| 805 | DO iv=1, ncol |
|---|
| 806 | SELECT CASE(iv) |
|---|
| 807 | CASE(1); r=> r0; CASE(2); r=> r1; CASE(3); r=> r2; CASE(4); r=> r3; CASE(5); r=> r4 |
|---|
| 808 | CASE(6); r=> r5; CASE(7); r=> r6; CASE(8); r=> r7; CASE(9); r=> r8; CASE(10);r=> r9 |
|---|
| 809 | END SELECT |
|---|
| 810 | n = SIZE(r, DIM=1) |
|---|
| 811 | IF(n/=nrow) THEN; CALL msg("Can't concatenate vectors of differing lengths"); STOP; END IF |
|---|
| 812 | out(:,iv) = r(:) |
|---|
| 813 | END DO |
|---|
| 814 | END FUNCTION horzcat_rm |
|---|
| 815 | !============================================================================================================================== |
|---|
| 816 | FUNCTION horzcat_d1(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9) RESULT(out) |
|---|
| 817 | DOUBLE PRECISION, TARGET, INTENT(IN) :: d0 |
|---|
| 818 | DOUBLE PRECISION, OPTIONAL, TARGET, INTENT(IN) :: d1, d2, d3, d4, d5, d6, d7, d8, d9 |
|---|
| 819 | DOUBLE PRECISION, ALLOCATABLE :: out(:) |
|---|
| 820 | DOUBLE PRECISION, POINTER :: d |
|---|
| 821 | LOGICAL :: lv(10) |
|---|
| 822 | INTEGER :: iv |
|---|
| 823 | lv = [ .TRUE. , PRESENT(d1), PRESENT(d2), PRESENT(d3), PRESENT(d4) , & |
|---|
| 824 | PRESENT(d5), PRESENT(d6), PRESENT(d7), PRESENT(d8), PRESENT(d9) ] |
|---|
| 825 | ALLOCATE(out(COUNT(lv))) |
|---|
| 826 | DO iv=1, COUNT(lv) |
|---|
| 827 | SELECT CASE(iv) |
|---|
| 828 | CASE(1); d=> d0; CASE(2); d=> d1; CASE(3); d=> d2; CASE(4); d=> d3; CASE(5); d=> d4 |
|---|
| 829 | CASE(6); d=> d5; CASE(7); d=> d6; CASE(8); d=> d7; CASE(9); d=> d8; CASE(10);d=> d9 |
|---|
| 830 | END SELECT |
|---|
| 831 | out(iv) = d |
|---|
| 832 | END DO |
|---|
| 833 | END FUNCTION horzcat_d1 |
|---|
| 834 | !============================================================================================================================== |
|---|
| 835 | FUNCTION horzcat_dm(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9) RESULT(out) |
|---|
| 836 | DOUBLE PRECISION, TARGET, DIMENSION(:), INTENT(IN) :: d0 |
|---|
| 837 | DOUBLE PRECISION, OPTIONAL, TARGET, DIMENSION(:), INTENT(IN) :: d1, d2, d3, d4, d5, d6, d7, d8, d9 |
|---|
| 838 | DOUBLE PRECISION, ALLOCATABLE :: out(:,:) |
|---|
| 839 | DOUBLE PRECISION, POINTER :: d(:) |
|---|
| 840 | LOGICAL :: lv(10) |
|---|
| 841 | INTEGER :: nrow, ncol, iv, n |
|---|
| 842 | lv = [ .TRUE. , PRESENT(d1), PRESENT(d2), PRESENT(d3), PRESENT(d4) , & |
|---|
| 843 | PRESENT(d5), PRESENT(d6), PRESENT(d7), PRESENT(d8), PRESENT(d9) ] |
|---|
| 844 | nrow = SIZE(d0); ncol=COUNT(lv) |
|---|
| 845 | ALLOCATE(out(nrow, ncol)) |
|---|
| 846 | DO iv=1, ncol |
|---|
| 847 | SELECT CASE(iv) |
|---|
| 848 | CASE(1); d=> d0; CASE(2); d=> d1; CASE(3); d=> d2; CASE(4); d=> d3; CASE(5); d=> d4 |
|---|
| 849 | CASE(6); d=> d5; CASE(7); d=> d6; CASE(8); d=> d7; CASE(9); d=> d8; CASE(10);d=> d9 |
|---|
| 850 | END SELECT |
|---|
| 851 | n = SIZE(d, DIM=1) |
|---|
| 852 | IF(n/=nrow) THEN; CALL msg("Can't concatenate vectors of differing lengths"); STOP; END IF |
|---|
| 853 | out(:,iv) = d(:) |
|---|
| 854 | END DO |
|---|
| 855 | END FUNCTION horzcat_dm |
|---|
| 856 | !============================================================================================================================== |
|---|
| 857 | |
|---|
| 858 | |
|---|
| 859 | !============================================================================================================================== |
|---|
| 860 | !--- Display a clean table composed of successive vectors of same length. |
|---|
| 861 | !=== The profile "p" describe in which order to pick up the columns from "s", "i" and "r" for display. |
|---|
| 862 | !============================================================================================================================== |
|---|
| 863 | LOGICAL FUNCTION dispTable(p, titles, s, i, r, rFmt, nmax, unit) RESULT(lerr) |
|---|
| 864 | CHARACTER(LEN=*), INTENT(IN) :: p !--- DISPLAY MAP: s/i/r |
|---|
| 865 | CHARACTER(LEN=*), INTENT(IN) :: titles(:) !--- TITLES (ONE EACH COLUMN) |
|---|
| 866 | CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: s(:,:) !--- STRINGS |
|---|
| 867 | INTEGER, OPTIONAL, INTENT(IN) :: i(:,:) !--- INTEGERS |
|---|
| 868 | REAL, OPTIONAL, INTENT(IN) :: r(:,:) !--- REALS |
|---|
| 869 | CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: rFmt !--- Format for reals |
|---|
| 870 | INTEGER, OPTIONAL, INTENT(IN) :: nmax !--- Display less than "nrow" rows |
|---|
| 871 | INTEGER, OPTIONAL, INTENT(IN) :: unit !--- Output unit (default: screen) |
|---|
| 872 | |
|---|
| 873 | CHARACTER(LEN=2048) :: row |
|---|
| 874 | CHARACTER(LEN=maxlen) :: rFm, el |
|---|
| 875 | CHARACTER(LEN=maxlen), ALLOCATABLE :: d(:,:) |
|---|
| 876 | CHARACTER(LEN=1) :: s1, sp |
|---|
| 877 | INTEGER :: is, ii, ir, np, nrow, unt, ic |
|---|
| 878 | INTEGER :: ns, ni, nr, ncol, nmx |
|---|
| 879 | INTEGER, ALLOCATABLE :: n(:) |
|---|
| 880 | INTEGER, PARAMETER :: nm=2 !--- Space between values & columns |
|---|
| 881 | LOGICAL :: ls, li, lr |
|---|
| 882 | |
|---|
| 883 | ! modname = 'dispTable' |
|---|
| 884 | rFm = '*'; IF(PRESENT(rFmt)) rFm = rFmt !--- Specified format for reals |
|---|
| 885 | unt = lunout; IF(PRESENT(unit)) unt = unit !--- Specified output unit |
|---|
| 886 | np = LEN_TRIM(p); ns = 0; ni = 0; nr = 0; ncol = 0 |
|---|
| 887 | ls = PRESENT(s); li = PRESENT(i); lr = PRESENT(r) |
|---|
| 888 | lerr = .FALSE.; IF(.NOT.ANY([ls,li,lr])) RETURN !--- Nothing to do |
|---|
| 889 | sp = '|' !--- Separator |
|---|
| 890 | |
|---|
| 891 | !--- CHECK ARGUMENTS COHERENCE |
|---|
| 892 | lerr = np /= SIZE(titles); IF(fmsg('string "pattern" length and titles list mismatch', ll=lerr)) RETURN |
|---|
| 893 | IF(ls) THEN; ns = SIZE(s, DIM=1); ncol = ncol + SIZE(s, DIM=2) |
|---|
| 894 | lerr = COUNT([(p(ic:ic)=='s', ic=1, np)]) /= SIZE(s, DIM=2) |
|---|
| 895 | END IF |
|---|
| 896 | IF(li) THEN; ni = SIZE(i, DIM=1); ncol = ncol + SIZE(i, DIM=2) |
|---|
| 897 | lerr = COUNT([(p(ic:ic)=='i', ic=1, np)]) /= SIZE(i, DIM=2) |
|---|
| 898 | END IF |
|---|
| 899 | IF(lr) THEN; nr = SIZE(r, DIM=1); ncol = ncol + SIZE(r, DIM=2) |
|---|
| 900 | lerr = COUNT([(p(ic:ic)=='r', ic=1, np)]) /= SIZE(r, DIM=2) |
|---|
| 901 | END IF |
|---|
| 902 | IF(fmsg('string "pattern" length and arguments number mismatch', ll=lerr)) RETURN |
|---|
| 903 | lerr = ncol /= SIZE(titles); IF(fmsg('"titles" length and arguments number mismatch', ll=lerr)) RETURN |
|---|
| 904 | lerr = ls.AND.li.AND.ns/=ni; IF(fmsg('string and integer arguments lengths mismatch', ll=lerr)) RETURN |
|---|
| 905 | lerr = ls.AND.lr.AND.ns/=nr; IF(fmsg( 'string and real arguments lengths mismatch', ll=lerr)) RETURN |
|---|
| 906 | lerr = li.AND.lr.AND.ni/=nr; IF(fmsg( 'integer and real arguments lengths mismatch', ll=lerr)) RETURN |
|---|
| 907 | nrow = MAX(ns,ni,nr)+1 |
|---|
| 908 | nmx = nrow; IF(PRESENT(nmax)) nmx = MIN(nmx,nmax+1) |
|---|
| 909 | |
|---|
| 910 | !--- Allocate the assembled quantities array |
|---|
| 911 | ALLOCATE(d(nrow,ncol), n(ncol)) |
|---|
| 912 | |
|---|
| 913 | !--- Assemble the vectors into a strings array in the order indicated by "pattern" |
|---|
| 914 | is = 1; ii = 1; ir = 1 |
|---|
| 915 | DO ic = 1, ncol |
|---|
| 916 | d(1,ic) = TRIM(titles(ic)) |
|---|
| 917 | SELECT CASE(p(ic:ic)) |
|---|
| 918 | CASE('s'); d(2:nmx,ic) = s(:,is) ; is = is + 1 |
|---|
| 919 | CASE('i'); d(2:nmx,ic) = int2str(i(:,ii) ); ii = ii + 1 |
|---|
| 920 | CASE('r'); d(2:nmx,ic) = real2str(r(:,ir),rFm); ir = ir + 1 |
|---|
| 921 | END SELECT |
|---|
| 922 | n(ic)=0; DO ir=1, nmx; n(ic)=MAX(n(ic), LEN_TRIM(d(ir,ic))); END DO |
|---|
| 923 | END DO |
|---|
| 924 | n(:) = n(:) + 2*nm |
|---|
| 925 | |
|---|
| 926 | !--- Display the strings array as a table |
|---|
| 927 | DO ir = 1, nmx; row = '' |
|---|
| 928 | DO ic = 1, ncol; el = d(ir,ic) |
|---|
| 929 | s1 = sp |
|---|
| 930 | row = TRIM(row)//REPEAT(' ',nm)//TRIM(el)//REPEAT(' ',n(ic)-LEN_TRIM(el)-nm)//s1 |
|---|
| 931 | END DO |
|---|
| 932 | nr = LEN_TRIM(row)-1 !--- Final separator removed |
|---|
| 933 | CALL msg(row(1:nr), unit=unt) |
|---|
| 934 | IF(ir /= 1) CYCLE !--- Titles are underlined |
|---|
| 935 | row=''; DO ic=1,ncol; row=TRIM(row)//REPEAT('-',n(ic))//'+'; END DO |
|---|
| 936 | CALL msg(row(1:LEN_TRIM(row)-1), unit=unt) |
|---|
| 937 | END DO |
|---|
| 938 | |
|---|
| 939 | END FUNCTION dispTable |
|---|
| 940 | !============================================================================================================================== |
|---|
| 941 | |
|---|
| 942 | !============================================================================================================================== |
|---|
| 943 | LOGICAL FUNCTION dispNamelist(unt, p, titles, s, i, r, rFmt, llast) RESULT(lerr) |
|---|
| 944 | INTEGER, INTENT(IN) :: unt !--- Output unit |
|---|
| 945 | CHARACTER(LEN=*), INTENT(IN) :: p !--- DISPLAY MAP: s/i/r |
|---|
| 946 | CHARACTER(LEN=*), INTENT(IN) :: titles(:) !--- TITLES (ONE EACH COLUMN) |
|---|
| 947 | CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: s(:,:) !--- STRINGS |
|---|
| 948 | INTEGER, OPTIONAL, INTENT(IN) :: i(:,:) !--- INTEGERS |
|---|
| 949 | REAL, OPTIONAL, INTENT(IN) :: r(:,:) !--- REALS |
|---|
| 950 | CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: rFmt !--- Format for reals |
|---|
| 951 | LOGICAL, OPTIONAL, INTENT(IN) :: llast !--- Last variable: no final ',' |
|---|
| 952 | |
|---|
| 953 | CHARACTER(LEN=maxlen) :: rFm, el |
|---|
| 954 | CHARACTER(LEN=maxlen), ALLOCATABLE :: d(:,:) |
|---|
| 955 | CHARACTER(LEN=:), ALLOCATABLE :: sp, row |
|---|
| 956 | INTEGER :: is, ii, ir, nrow, ic |
|---|
| 957 | INTEGER :: ns, ni, nr, ncol, np |
|---|
| 958 | INTEGER, ALLOCATABLE :: n(:) |
|---|
| 959 | LOGICAL :: ls, li, lr, la |
|---|
| 960 | |
|---|
| 961 | ! modname = 'dispNamelist' |
|---|
| 962 | rFm = '*'; IF(PRESENT(rFmt)) rFm = rFmt !--- Specified format for reals |
|---|
| 963 | ls = PRESENT(s); li = PRESENT(i); lr = PRESENT(r) |
|---|
| 964 | lerr = .FALSE.; IF(.NOT.ANY([ls,li,lr])) RETURN !--- Nothing to do |
|---|
| 965 | la = .FALSE.; IF(PRESENT(llast)) la = llast |
|---|
| 966 | |
|---|
| 967 | !--- CHECK ARGUMENTS COHERENCE |
|---|
| 968 | ns = 0; ni = 0; nr = 0; np = LEN_TRIM(p); ncol = 0 |
|---|
| 969 | IF(ls) THEN; ns = SIZE(s, DIM=1); ncol = ncol + SIZE(s, DIM=2) |
|---|
| 970 | lerr = COUNT([(p(ic:ic)=='s', ic=1, np)]) /= SIZE(s, DIM=2) |
|---|
| 971 | END IF |
|---|
| 972 | IF(li) THEN; ni = SIZE(i, DIM=1); ncol = ncol + SIZE(i, DIM=2) |
|---|
| 973 | lerr = COUNT([(p(ic:ic)=='i', ic=1, np)]) /= SIZE(i, DIM=2) |
|---|
| 974 | END IF |
|---|
| 975 | IF(lr) THEN; nr = SIZE(r, DIM=1); ncol = ncol + SIZE(r, DIM=2) |
|---|
| 976 | lerr = COUNT([(p(ic:ic)=='r', ic=1, np)]) /= SIZE(r, DIM=2) |
|---|
| 977 | END IF |
|---|
| 978 | IF(fmsg('string "pattern" length and arguments number mismatch', ll=lerr)) RETURN |
|---|
| 979 | lerr = ncol /= SIZE(titles); IF(fmsg('"titles" length and arguments number mismatch', ll=lerr)) RETURN |
|---|
| 980 | lerr = ls.AND.li.AND.ns/=ni; IF(fmsg('string and integer arguments lengths mismatch', ll=lerr)) RETURN |
|---|
| 981 | lerr = ls.AND.lr.AND.ns/=nr; IF(fmsg( 'string and real arguments lengths mismatch', ll=lerr)) RETURN |
|---|
| 982 | lerr = li.AND.lr.AND.ni/=nr; IF(fmsg( 'integer and real arguments lengths mismatch', ll=lerr)) RETURN |
|---|
| 983 | |
|---|
| 984 | !--- Allocate the assembled quantities array |
|---|
| 985 | nrow = MAX(ns,ni,nr)+1 |
|---|
| 986 | ALLOCATE(d(nrow,ncol), n(ncol)) |
|---|
| 987 | |
|---|
| 988 | !--- Assemble the vectors into a strings array in the order indicated by "pattern" |
|---|
| 989 | is = 1; ii = 1; ir = 1 |
|---|
| 990 | DO ic = 1, ncol |
|---|
| 991 | d(1,ic) = TRIM(titles(ic)) |
|---|
| 992 | SELECT CASE(p(ic:ic)) |
|---|
| 993 | CASE('s'); d(2:nrow,ic) = s(:,is) ; is = is + 1 |
|---|
| 994 | CASE('i'); d(2:nrow,ic) = int2str(i(:,ii) ); ii = ii + 1 |
|---|
| 995 | CASE('r'); d(2:nrow,ic) = real2str(r(:,ir),rFm); ir = ir + 1 |
|---|
| 996 | END SELECT |
|---|
| 997 | n(ic) = 0; DO ir=1, nrow; n(ic)=MAX(n(ic), LEN_TRIM(d(ir,ic))); END DO |
|---|
| 998 | IF(needQuotes(d(2,ic)) .AND. ic/=1) n(ic) = n(ic) + 2 !--- For quotes, using second line only |
|---|
| 999 | END DO |
|---|
| 1000 | |
|---|
| 1001 | !--- Display the strings array as a table |
|---|
| 1002 | DO ir = 1, nrow |
|---|
| 1003 | row = ''; sp = ' '; IF(TRIM(d(ir,1)) /= '') sp = ' = ' |
|---|
| 1004 | DO ic = 1, ncol |
|---|
| 1005 | el = d(ir,ic); IF(ic /= 1) el = addQuotes_1(el) |
|---|
| 1006 | row = row//TRIM(el)//REPEAT(' ',n(ic)-LEN_TRIM(el))//sp |
|---|
| 1007 | sp = ' '; IF(ic /= ncol-1) CYCLE |
|---|
| 1008 | IF(TRIM(d(MIN(ir+1,nrow),1)) /= '' .AND. (ir /= nrow .OR. .NOT.la)) sp = ' , ' |
|---|
| 1009 | END DO |
|---|
| 1010 | WRITE(unt,'(a)')TRIM(row) |
|---|
| 1011 | END DO |
|---|
| 1012 | |
|---|
| 1013 | !--- End of section |
|---|
| 1014 | IF(la) THEN |
|---|
| 1015 | WRITE(unt,'(a)')'/' |
|---|
| 1016 | WRITE(unt,'(a)') |
|---|
| 1017 | END IF |
|---|
| 1018 | |
|---|
| 1019 | END FUNCTION dispNameList |
|---|
| 1020 | !============================================================================================================================== |
|---|
| 1021 | |
|---|
| 1022 | |
|---|
| 1023 | !============================================================================================================================== |
|---|
| 1024 | LOGICAL FUNCTION dispOutliers_1(ll, a, n, err_msg, nam, subn, nmax, unit) RESULT(lerr) |
|---|
| 1025 | ! Display outliers list in tables |
|---|
| 1026 | ! If "nam" is supplied, it means the last index is for tracers => one table each tracer for rank > 2. |
|---|
| 1027 | LOGICAL, INTENT(IN) :: ll(:) !--- Linearized mask of outliers |
|---|
| 1028 | REAL, INTENT(IN) :: a(:) !--- Linearized array of values |
|---|
| 1029 | INTEGER, INTENT(IN) :: n(:) !--- Profile before linearization |
|---|
| 1030 | |
|---|
| 1031 | CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: err_msg, nam(:), subn !--- Error message, variables and calling subroutine names |
|---|
| 1032 | INTEGER, OPTIONAL, INTENT(IN) :: nmax, unit !--- Maximum number of lines to display (default: all) |
|---|
| 1033 | CHARACTER(LEN=maxlen), ALLOCATABLE :: ttl(:) |
|---|
| 1034 | LOGICAL, ALLOCATABLE :: m(:) |
|---|
| 1035 | INTEGER, ALLOCATABLE :: ki(:), kj(:) |
|---|
| 1036 | INTEGER :: i, j, k, rk, rk1, ib, ie, itr, nm, unt, nmx, nv |
|---|
| 1037 | CHARACTER(LEN=maxlen) :: mes, sub, fm='(f12.9)', v, s |
|---|
| 1038 | CHARACTER(LEN=maxlen), ALLOCATABLE :: vnm(:) |
|---|
| 1039 | |
|---|
| 1040 | lerr = ANY(ll); IF(.NOT.lerr) RETURN !--- No outliers -> finished |
|---|
| 1041 | |
|---|
| 1042 | mes = 'outliers found'; IF(PRESENT(err_msg)) mes = err_msg !--- Error message |
|---|
| 1043 | vnm = ['a']; IF(PRESENT(nam )) vnm = nam !--- Variables names |
|---|
| 1044 | sub = 'dispOutliers'; IF(PRESENT(subn)) sub = subn !--- Calling subroutine name |
|---|
| 1045 | nmx = SIZE(a); IF(PRESENT(nmax)) nmx = MIN(nmx,nmax) !--- Maximum number of lines to print |
|---|
| 1046 | unt = lunout; IF(PRESENT(unit)) unt = unit !--- Unit to print messages |
|---|
| 1047 | |
|---|
| 1048 | rk = SIZE(n); nv = SIZE(vnm) |
|---|
| 1049 | IF(test(fmsg('SIZE(nam) /= 1 and /= last "n" element', sub, nv /= 1 .AND. nv /= n(rk), unt),lerr)) RETURN |
|---|
| 1050 | IF(test(fmsg('ll" and "a" sizes mismatch', sub, SIZE(a) /= SIZE(ll), unt),lerr)) RETURN |
|---|
| 1051 | IF(test(fmsg('profile "n" does not match "a" and "ll', sub, SIZE(a) /= PRODUCT(n), unt),lerr)) RETURN |
|---|
| 1052 | CALL msg(mes, sub, unit=unt) |
|---|
| 1053 | |
|---|
| 1054 | !--- SCALAR CASE: single value to display |
|---|
| 1055 | IF(rk==1.AND.n(1)==1) THEN |
|---|
| 1056 | IF(ll(1)) WRITE(unt,'(a," = ",f12.9)')TRIM(nam(1)),a(1); RETURN |
|---|
| 1057 | END IF |
|---|
| 1058 | |
|---|
| 1059 | rk1 = rk; IF(nv==1) rk1 = rk-1 !--- Rank of each displayed table |
|---|
| 1060 | SELECT CASE(rk1) !--- Indices list |
|---|
| 1061 | CASE(1,2); ki = [ (i,i=1,n(1)) ] |
|---|
| 1062 | CASE(3); ki = [((i,i=1,n(1)),j=1,n(2))]; kj = [((j,i=1,n(1)),j=1,n(2))] |
|---|
| 1063 | CASE DEFAULT; WRITE(unt,*)'Sorry: routine "dispOutliers" is limited to rank 3'; RETURN |
|---|
| 1064 | END SELECT |
|---|
| 1065 | |
|---|
| 1066 | !--- VECTOR CASE: table " name | value " (known names) / ) / " i | a(i) " (unknown names) |
|---|
| 1067 | IF(rk==1) THEN |
|---|
| 1068 | ALLOCATE(ttl(2)); ttl(2) = TRIM(vnm(1))//'(i)'; ttl(1) = 'i' |
|---|
| 1069 | IF(nv == 1) lerr = dispTable('sr', ttl, s=cat(PACK(nam,ll)), r=cat(PACK(a,ll)), rFmt=fm, nmax=nmax) |
|---|
| 1070 | IF(nv /= 1) lerr = dispTable('ir', ['name ','value'], i=cat(PACK(ki,m)), r=cat(PACK(a,ll)), rFmt=fm, nmax=nmax) |
|---|
| 1071 | CALL msg("can't display outliers table", sub, lerr, unt) |
|---|
| 1072 | RETURN |
|---|
| 1073 | END IF |
|---|
| 1074 | |
|---|
| 1075 | !--- OTHER CASES: one table for each tracer (last index) |
|---|
| 1076 | ttl = [(ACHAR(k), k = 105, 104+rk)] !--- Titles list ('i', 'j', 'k'...) |
|---|
| 1077 | s = strStack( ttl(1:rk-1) ) !--- Tracer name dummy indexes: (i, j, k, ... |
|---|
| 1078 | |
|---|
| 1079 | DO itr=1,n(rk) |
|---|
| 1080 | nm = PRODUCT(n(1:rk-1)) !--- number of elements per tracer |
|---|
| 1081 | ie = itr*nm; ib = ie-nm+1; m=ll(ib:ie) !--- section bounds for tracer "itr" ; outlayers mask |
|---|
| 1082 | IF(.NOT.ANY(m)) CYCLE !--- no outlayers for tracer "itr" |
|---|
| 1083 | v = TRIM(vnm(MIN(itr,SIZE(vnm))))//'('//TRIM(s) !--- "<name>(" |
|---|
| 1084 | IF(nv == 1) ttl(rk) = TRIM(v)//','//int2str(itr)//')' !--- "<name>(i,j,itr)" (single name) |
|---|
| 1085 | IF(nv /= 1) ttl(rk) = TRIM(v)//')' !--- "<nam(itr)>(i,j)" (one name each table/itr index) |
|---|
| 1086 | IF(rk==2) lerr = dispTable('ir', ttl, i=cat(PACK(ki,m)), r=cat(PACK(a(ib:ie),m)), rFmt=fm, nmax=nmax) |
|---|
| 1087 | IF(rk==3) lerr = dispTable('iir', ttl, i=cat(PACK(ki,m),PACK(kj,m)), r=cat(PACK(a(ib:ie),m)), rFmt=fm, nmax=nmax) |
|---|
| 1088 | CALL msg("can't display outliers table", sub, lerr, unt) |
|---|
| 1089 | IF(lerr) RETURN |
|---|
| 1090 | END DO |
|---|
| 1091 | END FUNCTION dispOutliers_1 |
|---|
| 1092 | !============================================================================================================================== |
|---|
| 1093 | LOGICAL FUNCTION dispOutliers_2(ll, a, n, err_msg, nam, subn, nmax, unit) RESULT(lerr) |
|---|
| 1094 | ! Display outliers list in tables |
|---|
| 1095 | ! If "nam" is supplied and, it means the last index is for tracers => one table each tracer for rank > 2. |
|---|
| 1096 | LOGICAL, INTENT(IN) :: ll(:) !--- Linearized mask of outliers |
|---|
| 1097 | REAL, INTENT(IN) :: a(:,:) !--- Linearized arrays of values stacked along 2nd dim. |
|---|
| 1098 | INTEGER, INTENT(IN) :: n(:) !--- Profile before linearization |
|---|
| 1099 | CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: err_msg, nam(:), subn !--- Error message, variables and calling subroutine names |
|---|
| 1100 | INTEGER, OPTIONAL, INTENT(IN) :: nmax, unit !--- Maximum number of lines to display (default: all) |
|---|
| 1101 | |
|---|
| 1102 | CHARACTER(LEN=maxlen) :: mes, sub, fm='(f12.9)', prf |
|---|
| 1103 | CHARACTER(LEN=maxlen), ALLOCATABLE :: ttl(:), vnm(:) |
|---|
| 1104 | LOGICAL, ALLOCATABLE :: m(:) |
|---|
| 1105 | INTEGER, ALLOCATABLE :: ki(:), kj(:), kl(:) |
|---|
| 1106 | INTEGER :: i, j, k, rk, ib, ie, itr, nm, nv, unt, nmx, rk1 |
|---|
| 1107 | REAL, ALLOCATABLE :: val(:,:) |
|---|
| 1108 | |
|---|
| 1109 | lerr = ANY(ll); IF(.NOT.lerr) RETURN !--- No outliers -> finished |
|---|
| 1110 | rk = SIZE(n); nv = SIZE(a,2) |
|---|
| 1111 | mes = 'outliers found'; IF(PRESENT(err_msg)) mes = err_msg !--- Error message |
|---|
| 1112 | vnm = [(ACHAR(k+96),k=1,nv)]; IF(PRESENT(nam )) vnm = nam !--- Variables names |
|---|
| 1113 | sub = 'dispOutliers'; IF(PRESENT(subn)) sub = subn !--- Calling subroutine name |
|---|
| 1114 | nmx = SIZE(a); IF(PRESENT(nmax)) nmx = MIN(nmx,nmax)!--- Maximum number of lines to print |
|---|
| 1115 | unt = lunout; IF(PRESENT(unit)) unt = unit !--- Unit to print messages |
|---|
| 1116 | lerr = SIZE(vnm) /= nv; IF(fmsg('SIZE(nam) /= SIZE(a,2)', sub, lerr, unt)) RETURN |
|---|
| 1117 | lerr = SIZE(a,1) /= SIZE(ll); IF(fmsg('"ll" and "a" sizes mismatch', sub, lerr, unt)) RETURN |
|---|
| 1118 | lerr = SIZE(a,1) /= PRODUCT(n); IF(fmsg('profile "n" does not match "a" and "ll"', sub, lerr, unt)) RETURN |
|---|
| 1119 | |
|---|
| 1120 | SELECT CASE(rk1) !--- Indices list |
|---|
| 1121 | CASE(0); IF(ll(1)) THEN; WRITE(unt,'(a,", ",a," = ",2f12.9)')TRIM(vnm(1)),TRIM(vnm(2)),a(1,1),a(1,2); RETURN; END IF |
|---|
| 1122 | CASE(1); ki = [ (i,i=1,n(1)) ] |
|---|
| 1123 | CASE(2); ki = [ ((i,i=1,n(1)),j=1,n(2))]; kj = [ ((j,i=1,n(1)),j=1,n(2))] |
|---|
| 1124 | CASE(3); ki = [(((i,i=1,n(1)),j=1,n(2)),k=1,n(3))]; kj = [(((j,i=1,n(1)),j=1,n(2)),k=1,n(3))] |
|---|
| 1125 | kl = [(((k,i=1,n(1)),j=1,n(2)),k=1,n(3))] |
|---|
| 1126 | CASE DEFAULT; WRITE(unt,*)'Sorry: routine "dispOutliers_2" is limited to rank 3'; RETURN |
|---|
| 1127 | END SELECT |
|---|
| 1128 | |
|---|
| 1129 | ttl = [(ACHAR(k), k = 105, 104+rk), vnm] !--- Titles list ('i', 'j', 'k'...'var1', 'var2', ...) |
|---|
| 1130 | prf = REPEAT('i',rk)//REPEAT('r',nv) !--- Profile |
|---|
| 1131 | ALLOCATE(val(COUNT(ll),nv)); DO k=1, nv; val(:,k) = PACK(a(:,k),ll); END DO |
|---|
| 1132 | IF(rk == 1) lerr = dispTable(prf, ttl, i = cat(PACK(ki,ll)), r = val, rFmt=fm, nmax=nmax) |
|---|
| 1133 | IF(rk == 2) lerr = dispTable(prf, ttl, i = cat(PACK(ki,ll),PACK(kj,ll)), r = val, rFmt=fm, nmax=nmax) |
|---|
| 1134 | IF(rk == 3) lerr = dispTable(prf, ttl, i = cat(PACK(ki,ll),PACK(kj,ll),PACK(kl,ll)), & |
|---|
| 1135 | r = val, rFmt=fm, nmax=nmax) |
|---|
| 1136 | CALL msg("can't display outliers table", sub, lerr, unt) |
|---|
| 1137 | END FUNCTION dispOutliers_2 |
|---|
| 1138 | !============================================================================================================================== |
|---|
| 1139 | |
|---|
| 1140 | |
|---|
| 1141 | !============================================================================================================================== |
|---|
| 1142 | !=== Reduce an algebrical expression (basic operations and parenthesis) to a single number (string format) ==================== |
|---|
| 1143 | !============================================================================================================================== |
|---|
| 1144 | LOGICAL FUNCTION reduceExpr_1(str, val) RESULT(lerr) |
|---|
| 1145 | CHARACTER(LEN=*), INTENT(IN) :: str |
|---|
| 1146 | CHARACTER(LEN=maxlen), INTENT(OUT) :: val |
|---|
| 1147 | |
|---|
| 1148 | CHARACTER(LEN=maxlen) :: v |
|---|
| 1149 | CHARACTER(LEN=1024) :: s, vv |
|---|
| 1150 | CHARACTER(LEN=1024), ALLOCATABLE :: vl(:) |
|---|
| 1151 | INTEGER, ALLOCATABLE :: ip(:) |
|---|
| 1152 | INTEGER :: nl, nn, i, j, im, ix |
|---|
| 1153 | LOGICAL :: ll |
|---|
| 1154 | ! modname = 'reduceExpr_1' |
|---|
| 1155 | s = str |
|---|
| 1156 | |
|---|
| 1157 | !--- Check wether the parenthesis are correctly formed |
|---|
| 1158 | ll = strCount(s,'(',nl) |
|---|
| 1159 | ll = strCount(s,')',nn) |
|---|
| 1160 | lerr = nl /= nn |
|---|
| 1161 | IF(fmsg('Mismatching number of opening and closing parenthesis: '//TRIM(s), ll=lerr)) RETURN |
|---|
| 1162 | nl = 2*nl-1 |
|---|
| 1163 | |
|---|
| 1164 | !--- Build vectors ; vl: parenthesis-separated elements ; ip: parenthesis types (1: opening, 2: closing) |
|---|
| 1165 | ALLOCATE(ip(nl-1),vl(nl)) |
|---|
| 1166 | j = 1; im = 1 |
|---|
| 1167 | DO i = 1, LEN_TRIM(str) |
|---|
| 1168 | ix = INDEX('()', str(i:i)) |
|---|
| 1169 | IF(ix == 0) CYCLE |
|---|
| 1170 | ip(j) = ix |
|---|
| 1171 | vl(j) = str(im:i-1) |
|---|
| 1172 | j = j + 1; im = i + 1 |
|---|
| 1173 | END DO |
|---|
| 1174 | vl(j) = str(im:LEN_TRIM(str)) |
|---|
| 1175 | |
|---|
| 1176 | !--- Search for each opening/closing parenthesis pair |
|---|
| 1177 | DO WHILE(nl > 1) |
|---|
| 1178 | i = 1; DO WHILE(ip(i) /= 1 .OR. ip(i+1) /= 2); i = i + 1; END DO !IF(i > SIZE(ip)+1) EXIT;END DO |
|---|
| 1179 | IF(test(reduceExpr_basic(vl(i+1), v), lerr)) RETURN |
|---|
| 1180 | v = TRIM(vl(i))//TRIM(v); IF(i+2<=nl) v=TRIM(v)//TRIM(vl(i+2)) |
|---|
| 1181 | vv = v//REPEAT(' ',768) |
|---|
| 1182 | IF(i == 1) THEN; ip = ip(3:nl-1); vl = [ vv, vl(4 :nl)] |
|---|
| 1183 | ELSE IF(i == nl-1) THEN; ip = ip(1:nl-2); vl = [vl(1:nl-3), vv ] |
|---|
| 1184 | ELSE; ip = [ip(1: i-1), ip(i+2:nl-1)]; vl = [vl(1: i-1), vv, vl(i+3:nl)]; END IF |
|---|
| 1185 | nl = SIZE(vl) |
|---|
| 1186 | END DO |
|---|
| 1187 | lerr = reduceExpr_basic(vl(1), val) |
|---|
| 1188 | END FUNCTION reduceExpr_1 |
|---|
| 1189 | |
|---|
| 1190 | |
|---|
| 1191 | !============================================================================================================================== |
|---|
| 1192 | !=== Reduce a simple algebrical expression (basic operations, no parenthesis) to a single number (string format) ============== |
|---|
| 1193 | !============================================================================================================================== |
|---|
| 1194 | LOGICAL FUNCTION reduceExpr_basic(str, val) RESULT(lerr) |
|---|
| 1195 | CHARACTER(LEN=*), INTENT(IN) :: str |
|---|
| 1196 | CHARACTER(LEN=*), INTENT(OUT) :: val |
|---|
| 1197 | DOUBLE PRECISION, ALLOCATABLE :: vl(:) |
|---|
| 1198 | INTEGER, ALLOCATABLE :: id(:) |
|---|
| 1199 | CHARACTER(LEN=maxlen), ALLOCATABLE :: ky(:) |
|---|
| 1200 | CHARACTER(LEN=1), ALLOCATABLE :: op(:) |
|---|
| 1201 | |
|---|
| 1202 | CHARACTER(LEN=1024) :: s |
|---|
| 1203 | DOUBLE PRECISION :: v, vm, vp |
|---|
| 1204 | INTEGER :: i, ni, io |
|---|
| 1205 | |
|---|
| 1206 | ! modname = 'reduceExpr_basic' |
|---|
| 1207 | lerr = .FALSE. |
|---|
| 1208 | IF(is_numeric(str)) THEN; val=TRIM(str); RETURN; END IF |
|---|
| 1209 | op = ['^','/','*','+','-'] !--- List of recognized operations |
|---|
| 1210 | s = str |
|---|
| 1211 | IF(test(strParse_m(s, op, ky, .TRUE., id = id), lerr)) RETURN !--- Parse the values |
|---|
| 1212 | vl = str2dble(ky) !--- Conversion to doubles |
|---|
| 1213 | lerr = ANY(vl >= HUGE(1.d0)) |
|---|
| 1214 | IF(fmsg('Some values are non-numeric in: '//TRIM(s), ll=lerr)) RETURN !--- Non-numerical values found |
|---|
| 1215 | DO io = 1, SIZE(op) !--- Loop on known operators (order matters !) |
|---|
| 1216 | DO i = SIZE(id), 1, -1 !--- Loop on found operators |
|---|
| 1217 | ni = SIZE(id) |
|---|
| 1218 | IF(id(i) /= io) CYCLE !--- Current found operator is not op(io) |
|---|
| 1219 | vm = vl(i); vp = vl(i+1) !--- Couple of values used for current operation |
|---|
| 1220 | SELECT CASE(io) !--- Perform operation on the two values |
|---|
| 1221 | CASE(1); v = vm**vp !--- ^ |
|---|
| 1222 | CASE(2); v = vm/vp !--- / |
|---|
| 1223 | CASE(3); v = vm*vp !--- * |
|---|
| 1224 | CASE(4); v = vm+vp !--- + |
|---|
| 1225 | CASE(5); v = vm-vp !--- + |
|---|
| 1226 | END SELECT |
|---|
| 1227 | IF(i == ni) THEN; vl = [vl(1:ni-1), v]; ELSE; vl = [vl(1:i-1), v, vl(i+2:ni+1)]; END IF |
|---|
| 1228 | IF(i == ni) THEN; id = id(1:ni-1); ELSE; id = [id(1:i-1), id(i+1:ni )]; END IF |
|---|
| 1229 | END DO |
|---|
| 1230 | END DO |
|---|
| 1231 | val = dble2str(vl(1)) |
|---|
| 1232 | |
|---|
| 1233 | END FUNCTION reduceExpr_basic |
|---|
| 1234 | !============================================================================================================================== |
|---|
| 1235 | |
|---|
| 1236 | !============================================================================================================================== |
|---|
| 1237 | FUNCTION reduceExpr_m(str, val) RESULT(lerr) |
|---|
| 1238 | LOGICAL, ALLOCATABLE :: lerr(:) |
|---|
| 1239 | CHARACTER(LEN=*), INTENT(IN) :: str(:) |
|---|
| 1240 | CHARACTER(LEN=maxlen), ALLOCATABLE, INTENT(OUT) :: val(:) |
|---|
| 1241 | INTEGER :: i |
|---|
| 1242 | ALLOCATE(lerr(SIZE(str)),val(SIZE(str))) |
|---|
| 1243 | lerr(:) = [(reduceExpr_1(str(i), val(i)), i=1, SIZE(str))] |
|---|
| 1244 | END FUNCTION reduceExpr_m |
|---|
| 1245 | !============================================================================================================================== |
|---|
| 1246 | |
|---|
| 1247 | |
|---|
| 1248 | !============================================================================================================================== |
|---|
| 1249 | !=== Check whether a string is a number or not ================================================================================ |
|---|
| 1250 | !============================================================================================================================== |
|---|
| 1251 | ELEMENTAL LOGICAL FUNCTION is_numeric(str) RESULT(out) |
|---|
| 1252 | CHARACTER(LEN=*), INTENT(IN) :: str |
|---|
| 1253 | REAL :: x |
|---|
| 1254 | INTEGER :: e |
|---|
| 1255 | CHARACTER(LEN=12) :: fmt |
|---|
| 1256 | IF(TRIM(str) == '') THEN; out = .FALSE.; RETURN; END IF |
|---|
| 1257 | WRITE(fmt,'("(f",i0,".0)")') LEN_TRIM(str) |
|---|
| 1258 | READ(str,fmt,IOSTAT=e) x |
|---|
| 1259 | out = e==0 .AND. INDEX('Ee',str(LEN_TRIM(str):LEN_TRIM(str)))==0 |
|---|
| 1260 | IF(str == '') out = .FALSE. |
|---|
| 1261 | END FUNCTION is_numeric |
|---|
| 1262 | !============================================================================================================================== |
|---|
| 1263 | |
|---|
| 1264 | |
|---|
| 1265 | !============================================================================================================================== |
|---|
| 1266 | !=== Convert a string into a logical/integer integer or an integer/real into a string ========================================= |
|---|
| 1267 | !============================================================================================================================== |
|---|
| 1268 | ELEMENTAL LOGICAL FUNCTION str2bool(str) RESULT(out) |
|---|
| 1269 | CHARACTER(LEN=*), INTENT(IN) :: str |
|---|
| 1270 | INTEGER :: ierr |
|---|
| 1271 | READ(str,*,IOSTAT=ierr) out |
|---|
| 1272 | IF(ierr==0) RETURN |
|---|
| 1273 | out = ANY(['t ','true ','.true.','y ','yes ']==strLower(str)) |
|---|
| 1274 | END FUNCTION str2bool |
|---|
| 1275 | !============================================================================================================================== |
|---|
| 1276 | ELEMENTAL INTEGER FUNCTION str2int(str) RESULT(out) |
|---|
| 1277 | CHARACTER(LEN=*), INTENT(IN) :: str |
|---|
| 1278 | INTEGER :: ierr |
|---|
| 1279 | READ(str,*,IOSTAT=ierr) out |
|---|
| 1280 | IF(ierr/=0) out = -HUGE(1) |
|---|
| 1281 | END FUNCTION str2int |
|---|
| 1282 | !============================================================================================================================== |
|---|
| 1283 | ELEMENTAL REAL FUNCTION str2real(str) RESULT(out) |
|---|
| 1284 | CHARACTER(LEN=*), INTENT(IN) :: str |
|---|
| 1285 | INTEGER :: ierr |
|---|
| 1286 | READ(str,*,IOSTAT=ierr) out |
|---|
| 1287 | IF(ierr/=0) out = -HUGE(1.) |
|---|
| 1288 | END FUNCTION str2real |
|---|
| 1289 | !============================================================================================================================== |
|---|
| 1290 | ELEMENTAL DOUBLE PRECISION FUNCTION str2dble(str) RESULT(out) |
|---|
| 1291 | CHARACTER(LEN=*), INTENT(IN) :: str |
|---|
| 1292 | INTEGER :: ierr |
|---|
| 1293 | READ(str,*,IOSTAT=ierr) out |
|---|
| 1294 | IF(ierr/=0) out = -HUGE(1.d0) |
|---|
| 1295 | END FUNCTION str2dble |
|---|
| 1296 | !============================================================================================================================== |
|---|
| 1297 | ELEMENTAL CHARACTER(LEN=maxlen) FUNCTION bool2str(b) RESULT(out) |
|---|
| 1298 | LOGICAL, INTENT(IN) :: b |
|---|
| 1299 | WRITE(out,*)b |
|---|
| 1300 | out = ADJUSTL(out) |
|---|
| 1301 | END FUNCTION bool2str |
|---|
| 1302 | !============================================================================================================================== |
|---|
| 1303 | ELEMENTAL CHARACTER(LEN=maxlen) FUNCTION int2str(i, nDigits) RESULT(out) |
|---|
| 1304 | INTEGER, INTENT(IN) :: i |
|---|
| 1305 | INTEGER, OPTIONAL, INTENT(IN) :: nDigits |
|---|
| 1306 | WRITE(out,*)i |
|---|
| 1307 | out = ADJUSTL(out) |
|---|
| 1308 | IF(.NOT.PRESENT(nDigits)) RETURN |
|---|
| 1309 | IF(nDigits > LEN_TRIM(out)) out = REPEAT('0', nDigits - LEN_TRIM(out))//TRIM(out) |
|---|
| 1310 | END FUNCTION int2str |
|---|
| 1311 | !============================================================================================================================== |
|---|
| 1312 | ELEMENTAL CHARACTER(LEN=maxlen) FUNCTION real2str(r,fmt) RESULT(out) |
|---|
| 1313 | REAL, INTENT(IN) :: r |
|---|
| 1314 | CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: fmt |
|---|
| 1315 | IF( PRESENT(fmt)) WRITE(out,fmt)r |
|---|
| 1316 | IF(.NOT.PRESENT(fmt)) WRITE(out, * )r |
|---|
| 1317 | out = ADJUSTL(out) |
|---|
| 1318 | END FUNCTION real2str |
|---|
| 1319 | !============================================================================================================================== |
|---|
| 1320 | ELEMENTAL CHARACTER(LEN=maxlen) FUNCTION dble2str(d,fmt) RESULT(out) |
|---|
| 1321 | DOUBLE PRECISION, INTENT(IN) :: d |
|---|
| 1322 | CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: fmt |
|---|
| 1323 | IF( PRESENT(fmt)) WRITE(out,fmt)d |
|---|
| 1324 | IF(.NOT.PRESENT(fmt)) WRITE(out, * )d |
|---|
| 1325 | out = ADJUSTL(out) |
|---|
| 1326 | END FUNCTION dble2str |
|---|
| 1327 | !============================================================================================================================== |
|---|
| 1328 | |
|---|
| 1329 | |
|---|
| 1330 | !============================================================================================================================== |
|---|
| 1331 | FUNCTION addQuotes_1(s) RESULT(out) |
|---|
| 1332 | CHARACTER(LEN=*), INTENT(IN) :: s |
|---|
| 1333 | CHARACTER(LEN=:), ALLOCATABLE :: out |
|---|
| 1334 | IF(needQuotes(s)) THEN; out = "'"//TRIM(s)//"'"; ELSE; out = s; END IF |
|---|
| 1335 | END FUNCTION addQuotes_1 |
|---|
| 1336 | !============================================================================================================================== |
|---|
| 1337 | FUNCTION addQuotes_m(s) RESULT(out) |
|---|
| 1338 | CHARACTER(LEN=*), INTENT(IN) :: s(:) |
|---|
| 1339 | CHARACTER(LEN=:), ALLOCATABLE :: out(:) |
|---|
| 1340 | INTEGER :: k, n |
|---|
| 1341 | n = MAXVAL(LEN_TRIM(s), MASK=.TRUE.) |
|---|
| 1342 | ALLOCATE(CHARACTER(LEN=n) :: out(SIZE(s))) |
|---|
| 1343 | DO k=1,SIZE(s) |
|---|
| 1344 | IF(needQuotes(s(k))) THEN; out(k) = "'"//TRIM(s(k))//"'"; ELSE; out(k) = s(k); END IF |
|---|
| 1345 | END DO |
|---|
| 1346 | END FUNCTION addQuotes_m |
|---|
| 1347 | !============================================================================================================================== |
|---|
| 1348 | ELEMENTAL LOGICAL FUNCTION needQuotes(s) RESULT(out) |
|---|
| 1349 | CHARACTER(LEN=*), INTENT(IN) :: s |
|---|
| 1350 | CHARACTER(LEN=1) :: b, e |
|---|
| 1351 | out = .TRUE.; IF(TRIM(s) == '') RETURN |
|---|
| 1352 | b = s(1:1); e = s(MAX(1,LEN_TRIM(s)):MAX(1,LEN_TRIM(s))) |
|---|
| 1353 | out = .NOT.is_numeric(s) .AND. (b /= "'" .OR. e /= "'") .AND. ( b /= '"' .OR. e /= '"') |
|---|
| 1354 | END FUNCTION needQuotes |
|---|
| 1355 | !============================================================================================================================== |
|---|
| 1356 | |
|---|
| 1357 | |
|---|
| 1358 | !============================================================================================================================== |
|---|
| 1359 | !=== TEST WHETHER A FILE IS PRESENT OR NOT ==================================================================================== |
|---|
| 1360 | !============================================================================================================================== |
|---|
| 1361 | LOGICAL FUNCTION testFile_1(fname) RESULT(out) |
|---|
| 1362 | CHARACTER(LEN=*), INTENT(IN) :: fname |
|---|
| 1363 | INTEGER :: ierr |
|---|
| 1364 | OPEN(90, FILE=fname, FORM='formatted', STATUS='old', IOSTAT=ierr); CLOSE(99) |
|---|
| 1365 | out = ierr/=0 |
|---|
| 1366 | END FUNCTION testFile_1 |
|---|
| 1367 | !============================================================================================================================== |
|---|
| 1368 | FUNCTION testFile_m(fname) RESULT(out) |
|---|
| 1369 | LOGICAL, ALLOCATABLE :: out(:) |
|---|
| 1370 | CHARACTER(LEN=*), INTENT(IN) :: fname(:) |
|---|
| 1371 | INTEGER :: k |
|---|
| 1372 | out = [(testFile_1(fname(k)), k=1, SIZE(fname))] |
|---|
| 1373 | END FUNCTION testFile_m |
|---|
| 1374 | !============================================================================================================================== |
|---|
| 1375 | |
|---|
| 1376 | |
|---|
| 1377 | !============================================================================================================================== |
|---|
| 1378 | !=== DISPLAY "<message>: the following <items> are <reason>" FOLLOWED BY THE LIST OF <str> FOR WHICH <lerr>==T. =============== |
|---|
| 1379 | !============================================================================================================================== |
|---|
| 1380 | LOGICAL FUNCTION checkList(str, lerr, message, items, reason, nmax) RESULT(out) |
|---|
| 1381 | ! Purpose: Messages in case a list contains wrong elements (indicated by lerr boolean vector). |
|---|
| 1382 | ! Note: Return value "out" is .TRUE. if there are errors (ie at least one element of "lerr" is TRUE). |
|---|
| 1383 | CHARACTER(LEN=*), INTENT(IN) :: str(:) |
|---|
| 1384 | LOGICAL, INTENT(IN) :: lerr(:) |
|---|
| 1385 | CHARACTER(LEN=*), INTENT(IN) :: message, items, reason |
|---|
| 1386 | INTEGER, OPTIONAL, INTENT(IN) :: nmax |
|---|
| 1387 | CHARACTER(LEN=maxlen), ALLOCATABLE :: s(:) |
|---|
| 1388 | INTEGER :: i, nmx |
|---|
| 1389 | nmx = 256; IF(PRESENT(nmax)) nmx=nmax |
|---|
| 1390 | out = ANY(lerr); IF(.NOT.out) RETURN |
|---|
| 1391 | CALL msg(TRIM(message)//': the following '//TRIM(items)//' are '//TRIM(reason)//':') |
|---|
| 1392 | s = strStackm(PACK(str, MASK=lerr), ', ',nmx) |
|---|
| 1393 | DO i=1,SIZE(s,DIM=1); CALL msg(s(i)); END DO |
|---|
| 1394 | END FUNCTION checkList |
|---|
| 1395 | !============================================================================================================================== |
|---|
| 1396 | |
|---|
| 1397 | |
|---|
| 1398 | !============================================================================================================================== |
|---|
| 1399 | !=== Remove comment in line "str", ie all the characters from the first "#" sign found in "str". ============================== |
|---|
| 1400 | !============================================================================================================================== |
|---|
| 1401 | SUBROUTINE removeComment(str) |
|---|
| 1402 | CHARACTER(LEN=*), INTENT(INOUT) :: str |
|---|
| 1403 | INTEGER :: ix |
|---|
| 1404 | ix = INDEX(str,'# '); IF(ix /= 0) str = str(1:ix-1)//REPEAT(' ',LEN(str)-ix+1) |
|---|
| 1405 | END SUBROUTINE removeComment |
|---|
| 1406 | !============================================================================================================================== |
|---|
| 1407 | |
|---|
| 1408 | |
|---|
| 1409 | |
|---|
| 1410 | END MODULE strings_mod |
|---|