PROGRAM XPT_LOGK * ===================================================================== * Highly modified version of SOLPRINT, to extract and interpolate * log K's at T's and P's specifed by the user. * Copyright 2018, 2024 James Palandri * INP1 = 1 : SOLTHERM datafile * INP2 = 2 : Input datafile * IOUT1 = 10 : Output file * Input file format is T and P pairs separated by at least one blank * space or comma, followed by the names of derived species, * minerals, or gases, with no leading blanks, spelled exactly as * in SOLTHERM. * Current limit is 1000 T, P pairs. * --------------------------------------------------------------------- IMPLICIT NONE LOGICAL MINSWITCH, PT_FOUND, TWARN INTEGER INP1, INP2, IOUT1, PINDEX, TINDEX, TIMEVAL(8), ITEMPC INTEGER BSTOT, I, J, K, ISOL, ISPEC(9), ITOT, JJ, KK, L CHARACTER * 1 PT_ERROR, ISO_ERROR CHARACTER * 3 MONTH(12) CHARACTER * 4 PTFLAG CHARACTER * 8 DAT(10), REF, SPECNM(100), DATE CHARACTER * 10 TIME CHARACTER * 20 INP_NAME, NAME, NAME2 CHARACTER * 30 FORMULA CHARACTER * 80 DUM CHARACTER * 120 TITLE1, TITLE2 REAL*8 B, CHARGE, CHG(100), COEF(9), DENS, VOL, MAXT REAL*8 WEIGHT, WT(100), ZZILCH(37,36), ZILCH(15) REAL*8 TEMPC(1000), PRESS(1000), YILCH(8), VAL, LGK COMMON /STUFF/ CHG, COEF, WT, ISPEC, SPECNM DATA MONTH /'Jan','Feb','Mar','Apr','May','Jun','Jul','Aug', + 'Sep','Oct','Nov','Dec'/ DATA PRESS, TEMPC /1000*0.d0,1000*0.d0 / * =====================================================================| PARAMETER (INP1 = 1, INP2 = 2, IOUT1 = 10) OPEN (INP1, FILE = 'SOLTHERM.XPT', STATUS = 'OLD') OPEN (INP2, FILE = 'XPT-logk.dat', STATUS = 'OLD') OPEN (IOUT1, FILE = 'XPT-LogK.out', STATUS = 'UNKNOWN') MINSWITCH = .FALSE. WEIGHT = 0.0 BSTOT = 0 FORMULA = ' ' INP_NAME = ' ' NAME = ' ' NAME2 = ' ' READ (INP1, *, ERR=277) BSTOT 277 IF (BSTOT .LE. 0) THEN WRITE (*,'(//2X, A)') + 'Input total number of component species in soltherm >>' READ (*,*) BSTOT END IF WRITE (*,*) * ---------------------------------------------------------------------| * Down to statement 40 we skip all the records before the component| * species, then read these species in | * ---------------------------------------------------------------------| 10 CONTINUE READ (INP1, '(A80)', END = 20) DUM IF (DUM(1:8) .NE. 'H+ ') GO TO 10 GO TO 30 20 CONTINUE WRITE (*,'(//2x,A,A)') 'ERROR: EOF encountered before ', + 'reaching H+ entry line in soltherm ***********' STOP 30 CONTINUE READ (DUM, 500,ERR=777) SPECNM(1), CHG(1), WT(1) 500 FORMAT (A8, 12X, F2.0, 17X, F8.4) DO 40 I = 2, BSTOT READ (INP1, 500,ERR=777) SPECNM(I), CHG(I), WT(I) 40 CONTINUE *** Read user name, write with date. CALL DATE_AND_TIME(VALUES=TIMEVAL) READ (INP2, '(A120)') TITLE1 READ (INP2, '(A120)') TITLE2 WRITE (IOUT1, '(A120)') TITLE1 WRITE (IOUT1, '(A120)') TITLE2 IF (TIMEVAL(3).GE.10) THEN WRITE (IOUT1,'(A,I4,A,A,A,I2,A,A)') + 'Log K''s extracted from Soltherm on ', + TIMEVAL(1),'-',MONTH(TIMEVAL(2)),'-',TIMEVAL(3) ELSE WRITE (IOUT1,'(A,I4,A,A,A,I1,A,A)') + 'Log K''s extracted from Soltherm on ', + TIMEVAL(1),'-',MONTH(TIMEVAL(2)),'-',TIMEVAL(3) ENDIF * ---------------------------------------------------------------------| * Interpolate logK's | * ---------------------------------------------------------------------| *** Read input file for T's and P's JJ = 0 110 JJ = JJ + 1 READ (INP2, *, ERR = 140, END = 3999) TEMPC(JJ), PRESS(JJ) GOTO 110 140 JJ = JJ - 1 BACKSPACE INP2 READ (INP2,'(A80)') DUM WRITE (*,*) WRITE (*,'(A)') ' Reading T, P pairs.' WRITE (*,'(A)') ' Non-numeric character found in line:' WRITE (*,'(4x,A)') DUM WRITE (*,*) WRITE (*,'(A)') ' Reading mineral, gas, and species names,' WRITE (*,'(A)') ' and calculating Log K''s' BACKSPACE INP2 *** If Non-numeric character, then current record is a species, mineral, *** or gas. Rewind soltherm, reset variables. 50 REWIND INP1 MINSWITCH = .FALSE. WEIGHT = 0.0 CHARGE = 0.0 DO 85 I = 1, 9 COEF(I) = 0.0 ISPEC(I) = 0 85 CONTINUE READ (INP2,'(A20)',END=3999) INP_NAME *** Find in SOLTHERM 155 READ (INP1,'(A20)',END=2999) NAME IF (NAME(1:7).EQ.'B12 COE') MINSWITCH = .TRUE. 157 IF (NAME.EQ.INP_NAME) THEN BACKSPACE INP1 WRITE (IOUT1, *) IF (MINSWITCH.EQV..FALSE.) THEN *** Then is aqs. species, read header READ (INP1, '(A20,A30,T91,A4)',ERR=777) + NAME, FORMULA, PTFLAG READ (INP1, '(8X,F3.0,T74,A8)',ERR=777) CHARGE, REF READ (INP1, '(I2,1X,9(2x,F8.3,I2))',ERR=777) + ITOT, (COEF(I), ISPEC(I),I = 1, ITOT) *** If H+ is not in the I = 1 position, flag it DO 60 I = 1, ITOT IF(I.GT.1.AND.ISPEC(I).EQ.1) THEN WRITE(IOUT1,663) NAME WRITE(*,663) NAME 663 FORMAT (' WARNING: Species ',A20, ' has H+ in a ', + 'position beyond I = 2.') ENDIF 60 CONTINUE ELSE *** Else is a gas or mineral, read header READ (INP1, '(A20,A30,T67,I2,T91,A4)',ERR=777) + NAME, FORMULA, ISOL, PTFLAG READ (INP1,'(T13,F8.3,T37,F8.3,T58,F7.3,T74,A8)',ERR=777) + WEIGHT, VOL, DENS, REF READ (INP1, '(I2,1X,9(2x,F8.3,I2))',ERR=777) + ITOT, (COEF(I), ISPEC(I),I = 1, ITOT) IF (ISOL .EQ. 6) READ (INP1, *) ENDIF IF (PTFLAG .EQ. 'All ') THEN *** Then read the full array DO 992, J = 1,37 READ (INP1,*,ERR=777) (ZZILCH(J,K), K = 1,36) 992 CONTINUE ELSE *** Else read the one line of L-V data READ (INP1,*,ERR=777) (YILCH(L), L = 1, 8) ENDIF ELSE GOTO 155 ENDIF *** Write header, stochiometry write (iout1,*) CALL PRINT (FORMULA, NAME, ITOT, CHARGE, WEIGHT, MINSWITCH) IF (MINSWITCH.EQV..TRUE.) THEN WRITE (IOUT1,'(2X, A10,F7.2,2X,A20,F8.2,2X,A13, F5.2)') + 'mol.wt. = ',WEIGHT,' mol.vol.(cc/mol) = ', + VOL,' rho(g/cc) = ',DENS ENDIF WRITE (IOUT1,'(2A8)') " Ref = ", REF WRITE (IOUT1,*) WRITE (IOUT1,'(A)') ' T(°C) P(bar) Log(K)' *** Interpolate logK's, write to output DO 1999, KK = 1, JJ IF (PTFLAG .EQ. 'All ') THEN *** Then full grid PT_ERROR = 'N' ISO_ERROR = 'N' CALL PT_INDEXER (PRESS(KK), TEMPC(KK), PINDEX, TINDEX, + PT_FOUND, PT_ERROR, ISO_ERROR) IF (PT_ERROR.EQ.'E'.OR.ISO_ERROR.EQ.'E') THEN WRITE (*,'(2X,A6,A20)') 'Entry ',NAME WRITE (*,994) TEMPC(KK), PRESS(KK) WRITE (IOUT1,994) TEMPC(KK), PRESS(KK) 994 FORMAT (1X,F7.2,2X,F8.3,1X + '99999.999 ERROR: Specified P and/or T are', + ' outside the P-T range of the data.') GOTO 1999 ENDIF CALL INTRP3D(PRESS(KK),TEMPC(KK),PINDEX,TINDEX,ZZILCH,VAL) LGK=VAL IF (PT_ERROR.EQ.'W'.OR.ISO_ERROR.EQ.'W') THEN WRITE (*,'(2X,A6,A20)') 'Entry ',NAME WRITE (IOUT1,999) TEMPC(KK), PRESS(KK), LGK WRITE (*,999) TEMPC(KK), PRESS(KK), LGK 999 FORMAT (1X,F7.2,2X,F8.3,1X,F9.3,2X'WARNING: P and/or T', + ' are in a grid block at the limit of the P-T', + ' range of data.') ELSE WRITE (IOUT1, '(1X,F7.2,2X,F8.3,1X,F9.3)') + TEMPC(KK), PRESS(KK), LGK ENDIF ELSE *** Else L-V saturation only CALL INTRP2D (TEMPC(KK), TWARN, MAXT, YILCH, VAL) LGK=VAL IF (YILCH(1).GT.99999.)THEN WRITE (*,'(2X,A6,A20)') 'Entry ',NAME WRITE (*,710) TEMPC(KK), PRESS(KK), LGK WRITE (IOUT1,710) TEMPC(KK), PRESS(KK),LGK 710 FORMAT (1X,F7.2,2X,F8.3,1x,F9.3 + ' ERROR: format error or data are ', + ' absent in soltherm') ELSEIF (MAXT.LT.(TEMPC(KK)-50.D0)) THEN ITEMPC = INT(MAXT) WRITE (*,'(2X,A6,A20)') 'Entry ',NAME WRITE (*,720) TEMPC(KK), PRESS(KK), LGK, ITEMPC WRITE (IOUT1,720) TEMPC(KK), PRESS(KK), LGK, ITEMPC 720 FORMAT (1X,F7.2,2X,F8.3,1X,F9.3, + ' ERROR: Data extend to ',I3,' °C only. Max', + ' extrapolation is 50 °C') ELSEIF (TEMPC(KK).LT.25.D0.OR.TEMPC(KK).GT.350.D0) THEN WRITE (*,'(2X,A6,A20)') 'Entry ',NAME WRITE (*,730) TEMPC(KK), PRESS(KK), LGK WRITE (IOUT1,730) TEMPC(KK), PRESS(KK), LGK 730 FORMAT (1X,F7.2,2X,F8.3,1X,F9.3, + ' WARNING: LogK extrapolated. Data limited to', + ' L-V saturation where 25 °C <= T <= 350 °C.') ELSE WRITE (IOUT1, '(1X,F7.2,2X,F8.3,1X,F9.3,2X,A,A)') + TEMPC(KK), PRESS(KK), LGK, 'Data for L-V sat', + ' only, pressure ignored.' ENDIF ENDIF 1999 CONTINUE *** Check for second soltherm entry in case of redox READ (INP1,'(/,A20)') NAME IF (NAME.EQ.INP_NAME) GOTO 157 GO TO 50 *** If input species or mineral not found in soltherm 2999 WRITE (IOUT1, *) WRITE (IOUT1, '(2X,A,A,A)') 'Entry ''',INP_NAME, + ''' not found in SOLTHERM.' WRITE (*, '(2X,A,A,A)') 'Entry ''',INP_NAME, + ''' not found in SOLTHERM.' GOTO 50 777 WRITE (*,'(2X,A25,A20)') 'Format error in record = ',NAME CLOSE (INP1) CLOSE (INP2) CLOSE (IOUT1) STOP 3999 CLOSE (INP1) CLOSE (INP2) CLOSE (IOUT1) WRITE (*,'(//,A,/)') ' DONE!' END SUBROUTINE PRINT (FORMULA, NAME, ITOT, CHARGE, WEIGHT, MINSWITCH) * =====================================================================| IMPLICIT NONE INTEGER INP1, IOUT1 INTEGER I, IPROD, IREAC, ISPEC(9), ITOT, PP, TRIGHT, TRIM CHARACTER * 3 CHAR1(9), CHAR2(9) CHARACTER * 8 PRODNM(9), REACNM(9), SPECNM(100) CHARACTER * 20 NAME CHARACTER * 30 FORMULA, REACNL, BLANK30 CHARACTER * 92 BLANK, BUFFER LOGICAL MINSWITCH REAL*8 CHARGE, CHBAL, CHG(100), COEF(9), PRODCF(9) REAL*8 REACF(9), WEIGHT, WT(100), WTM EXTERNAL TRIGHT COMMON /STUFF/ CHG, COEF, WT, ISPEC, SPECNM DATA CHAR1 /' ', 8*' + '/ DATA CHAR2 /' = ', 8*' + '/ DATA BLANK /' '/ DATA BLANK30 /' '/ * =====================================================================| INP1 = 1 IOUT1 = 10 IREAC = 1 IPROD = 0 CHBAL = 0.0 WTM = 0.0 REACF(1) = 1.0 REACNL = BLANK30 IF (FORMULA.NE.BLANK30) THEN IF (MINSWITCH.EQV..TRUE.) THEN WRITE (IOUT1,'(1X,A20)') NAME ENDIF REACNL = FORMULA ELSE REACNL(1:20) = NAME ENDIF DO 40 I = 1, ITOT * Add charges and weights of components species to check for balance, below. CHBAL = CHBAL + COEF(I) * CHG(ISPEC(I)) WTM = WTM + COEF(I) * WT(ISPEC(I)) * Parse positive and negative charge for components with branched IF IF (COEF(I)) 10, 10, 20 10 CONTINUE IREAC = IREAC + 1 REACF(IREAC) = -1.0 * COEF(I) REACNM(IREAC) = SPECNM(ISPEC(I)) GO TO 30 20 IPROD = IPROD + 1 PRODCF(IPROD) = COEF(I) PRODNM(IPROD) = SPECNM(ISPEC(I)) 30 CONTINUE 40 CONTINUE * ---------------------------------------------------------------------| * prints reaction in readable form | * ---------------------------------------------------------------------| PP = 1 BUFFER = ' ' DO 43 I = 1, IREAC IF (I .NE. 1) THEN WRITE (BUFFER(PP:), '(A3)') CHAR1(I) PP = PP + 3 END IF IF (TRIGHT(REACF(I)) .EQ. 1) THEN WRITE (BUFFER(PP:), '(F4.1)') REACF(I) PP = PP + 4 ELSE IF (TRIGHT(REACF(I)) .EQ. 2) THEN WRITE (BUFFER(PP:), '(F5.2)') REACF(I) PP = PP + 5 ELSE WRITE (BUFFER(PP:), '(F6.3)') REACF(I) PP = PP + 6 END IF IF (I.EQ.1) THEN WRITE (BUFFER(PP:), '(2X, A30)') REACNL TRIM = LEN_TRIM(REACNL) PP = PP + TRIM + 3 ELSE WRITE (BUFFER(PP:), '(1X, A8)') REACNM(I) TRIM = LEN_TRIM(REACNM(I)) PP = PP + TRIM + 2 ENDIF IF (PP + 20 .GE. 92) THEN WRITE (IOUT1, '(A92)') BUFFER PP = 2 BUFFER = ' ' END IF 43 CONTINUE IF (BUFFER .NE. BLANK) THEN WRITE (IOUT1, '(A92)') BUFFER END IF PP = 5 BUFFER = ' ' DO 45 I = 1, IPROD WRITE (BUFFER(PP:), '(A3)') CHAR2(I) PP = PP + 3 IF (TRIGHT(PRODCF(I)) .EQ. 1) THEN WRITE (BUFFER(PP:), '(F4.1)') PRODCF(I) PP = PP + 4 ELSE IF (TRIGHT(PRODCF(I)) .EQ. 2) THEN WRITE (BUFFER(PP:), '(F5.2)') PRODCF(I) PP = PP + 5 ELSE WRITE (BUFFER(PP:), '(F6.3)') PRODCF(I) PP = PP + 6 END IF WRITE (BUFFER(PP:), '(1X, A8)') PRODNM(I) TRIM = LEN_TRIM(PRODNM(I)) PP = PP + TRIM + 2 IF (PP + 20 .GE. 92) THEN WRITE (IOUT1, '(A92)') BUFFER PP = 5 BUFFER = ' ' END IF 45 CONTINUE IF (BUFFER .NE. BLANK) THEN WRITE (IOUT1, '(A92)') BUFFER END IF CHBAL = CHBAL - CHARGE WTM = WTM - WEIGHT IF (ABS(CHBAL) .LE. 0.000010) GO TO 50 WRITE (IOUT1, 412) CHBAL 412 FORMAT (/5X,' *** WARNING: charge balance is: ',F7.3/) WRITE (*,413) CHBAL, NAME 413 FORMAT(/5X,' *** WARNING: charge balance is: ',F7.3,' FOR ',A20) 50 CONTINUE IF(WEIGHT.EQ.0.) RETURN IF(ABS(WTM).LT.0.1) RETURN WRITE (IOUT1, 415) WTM 415 FORMAT(/5X,' *** WARNING: mol.weight balance is: ',F8.3/) WRITE (*,416) WTM, NAME 416 FORMAT(/5X,'*** WARNING: mol.weight balance is: ',F8.3, + ' FOR ',A20) RETURN END INTEGER FUNCTION TRIGHT (STUFF) * =====================================================================| * Returns 1 (for 0 or 1), 2 or 3, the number of significant figures for * reaction coefficients, to minimize the number of blanks. REAL * 8 CPUMIN, FRACTN, NUMBER, STUFF PARAMETER (CPUMIN = 1.0D-35) INTRINSIC DINT, DABS * =====================================================================| NUMBER = STUFF FRACTN = DINT(NUMBER) - NUMBER IF (DABS(FRACTN) .LE. CPUMIN) THEN TRIGHT = 1 RETURN END IF NUMBER = NUMBER * 10 FRACTN = DINT(NUMBER) - NUMBER IF (DABS(FRACTN) .LE. CPUMIN) THEN TRIGHT = 1 RETURN END IF NUMBER = NUMBER * 10 FRACTN = DINT(NUMBER) - NUMBER IF (DABS(FRACTN) .LE. CPUMIN) THEN TRIGHT = 2 RETURN END IF TRIGHT = 3 RETURN END SUBROUTINE PT_INDEXER (P,T,PIDX,TIDX,PT_FOUND,PT_ERROR,ISO_ERROR) *======================================================================| * Finds array indices PIDX and TIDX using specified P, T * Copyright 2023 James Palandri * James Palandri hereby disclaims all copyright interest in the * Subroutine "PT_INDEXER" (which defines a range of temperatures * and pressures in which subsequent calculation are valid). * written by James Palandri * Signature of James Palandri, 31 January 2024 * James Palandri * This file is part of subroutine PT_INDEXER. * PT_INDEXER is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. PT_INDEXER is distributed in * the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more * details. You should have received a copy of the GNU * General Public License along with SuprxnGT. If not, see * . *----------------------------------------------------------------------| IMPLICIT NONE LOGICAL PT_FOUND INTEGER PIDX, TIDX CHARACTER PT_ERROR, ISO_ERROR REAL*8 P, T *======================================================================| **** Assign array index integer for pressure (PIDX). **** PT_ERROR = 'N' if no warning **** PT_ERROR = 'W' if P > 0.0 and P < 1.0 or **** if P > 5000.0 and P < 6000.0 or **** if T > 600.0 and T <= 700.0 **** PT_ERROR = 'E' if P <= 0.0 or **** if P >= 6000.0 or **** if T > 700.0 or **** if T < 0.01 PT_ERROR = 'N' IF (P.LE.0.0D0) THEN PIDX = 1 PT_ERROR = 'E' ELSE IF (P.GT.0.0D0.AND.P.LT.1.0D0) THEN PIDX = 1 PT_ERROR = 'W' ELSE IF (P.GE.1.0D0.AND.P.LT.1.0133D0) THEN PIDX = 1 ELSE IF (P.GE.1.0133D0.AND.P.LT.2.321D0) THEN PIDX = 2 ELSE IF (P.GE.2.321D0.AND.P.LT.4.758D0) THEN PIDX = 3 ELSE IF (P.GE.4.758D0.AND.P.LT.8.919D0) THEN PIDX = 4 ELSE IF (P.GE.8.919D0.AND.P.LT.15.537D0) THEN PIDX = 5 ELSE IF (P.GE.15.537D0.AND.P.LT.25.479D0) THEN PIDX = 6 ELSE IF (P.GE.25.479D0.AND.P.LT.39.737D0) THEN PIDX = 7 ELSE IF (P.GE.39.737D0.AND.P.LT.59.432D0) THEN PIDX = 8 ELSE IF (P.GE.59.432D0.AND.P.LT.85.839D0) THEN PIDX = 9 ELSE IF (P.GE.85.839D0.AND.P.LT.120.458D0) THEN PIDX = 10 ELSE IF (P.GE.120.458D0.AND.P.LT.165.212D0) THEN PIDX = 11 ELSE IF (P.GE.165.212D0.AND.P.LT.200.0D0) THEN PIDX = 12 ELSE IF (P.GE.200.0D0.AND.P.LT.250.0D0) THEN PIDX = 13 ELSE IF (P.GE.250.0D0.AND.P.LT.300.0D0) THEN PIDX = 14 ELSE IF (P.GE.300.0D0.AND.P.LT.350.0D0) THEN PIDX = 15 ELSE IF (P.GE.350.0D0.AND.P.LT.400.0D0) THEN PIDX = 16 ELSE IF (P.GE.400.0D0.AND.P.LT.450.0D0) THEN PIDX = 17 ELSE IF (P.GE.450.0D0.AND.P.LT.500.0D0) THEN PIDX = 18 ELSE IF (P.GE.500.0D0.AND.P.LT.550.0D0) THEN PIDX = 19 ELSE IF (P.GE.550.0D0.AND.P.LT.600.0D0) THEN PIDX = 20 ELSE IF (P.GE.600.0D0.AND.P.LT.650.0D0) THEN PIDX = 21 ELSE IF (P.GE.650.0D0.AND.P.LT.700.0D0) THEN PIDX = 22 ELSE IF (P.GE.700.0D0.AND.P.LT.750.0D0) THEN PIDX = 23 ELSE IF (P.GE.750.0D0.AND.P.LT.800.0D0) THEN PIDX = 24 ELSE IF (P.GE.800.0D0.AND.P.LT.850.0D0) THEN PIDX = 25 ELSE IF (P.GE.850.0D0.AND.P.LT.900.0D0) THEN PIDX = 26 ELSE IF (P.GE.900.0D0.AND.P.LT.950.0D0) THEN PIDX = 27 ELSE IF (P.GE.950.0D0.AND.P.LT.1000.0D0) THEN PIDX = 28 ELSE IF (P.GE.1000.0D0.AND.P.LT.1500.0D0) THEN PIDX = 29 ELSE IF (P.GE.1500.0D0.AND.P.LT.2000.0D0) THEN PIDX = 30 ELSE IF (P.GE.2000.0D0.AND.P.LT.2500.0D0) THEN PIDX = 31 ELSE IF (P.GE.2500.0D0.AND.P.LT.3000.0D0) THEN PIDX = 32 ELSE IF (P.GE.3000.0D0.AND.P.LT.3500.0D0) THEN PIDX = 33 ELSE IF (P.GE.3500.0D0.AND.P.LT.4000.0D0) THEN PIDX = 34 ELSE IF (P.GE.4000.0D0.AND.P.LE.5000.0D0) THEN PIDX = 35 ELSE IF (P.GT.5000.0D0.AND.P.LT.6000.0D0) THEN PIDX = 35 PT_ERROR = 'W' ELSE IF (P.GE.6000.0D0) THEN PIDX = 35 PT_ERROR = 'E' ENDIF **** Assign array index integer for temperature (TIDX) **** ISO_ERROR = 'N' if no warning **** ISO_ERROR = 'W' if P/T near L-V saturation or **** if P/T near critical isochore, rho H2O < 0.35g/cc **** ISO_ERROR = 'E' if P/T fully in the two-phase region or **** if P/T fully above critical isochore. IF (T.LT.0.01D0) THEN PT_ERROR = 'E' TIDX = 3 IF (PIDX.GT.34) PIDX = 34 ELSE IF (T.GE.0.01D0.AND.T.LE.50.0D0) THEN TIDX = 3 IF (PIDX.GT.34) PIDX = 34 ELSE IF (T.GT.50.0D0.AND.T.LE.75.0D0) THEN TIDX = 4 ELSE IF (T.GT.75.0D0.AND.T.LE.100.0D0) THEN TIDX = 5 IF (P.LT.1.0133D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF ELSE IF (T.GT.100.0D0.AND.T.LE.125.0D0) THEN TIDX = 6 IF (P.LT.2.321D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.1.013D0) ISO_ERROR = 'E' ELSE IF (T.GT.125.0D0.AND.T.LE.150.0D0) THEN TIDX = 7 IF (P.LT.4.758D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.2.321D0) ISO_ERROR = 'E' ELSE IF (T.GT.150.0D0.AND.T.LE.175.0D0) THEN TIDX = 8 IF (P.LT.8.919D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.4.758D0) ISO_ERROR = 'E' ELSE IF (T.GT.175.0D0.AND.T.LE.200.0D0) THEN TIDX = 9 IF (P.LT.15.537D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.8.919D0) ISO_ERROR = 'E' ELSE IF (T.GT.200.0D0.AND.T.LE.225.0D0) THEN TIDX = 10 IF (P.LT.25.479D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.15.537D0) ISO_ERROR = 'E' ELSE IF (T.GT.225.0D0.AND.T.LE.250.0D0) THEN TIDX = 11 IF (P.LT.39.737D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.25.479D0) ISO_ERROR = 'E' ELSE IF (T.GT.250.0D0.AND.T.LE.275.0D0) THEN TIDX = 12 IF (P.LT.59.432D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.39.737D0) ISO_ERROR = 'E' ELSE IF (T.GT.275.0D0.AND.T.LE.300.0D0) THEN TIDX = 13 IF (P.LT.85.839D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.59.432D0) ISO_ERROR = 'E' ELSE IF (T.GT.300.0D0.AND.T.LE.325.0D0) THEN TIDX = 14 IF (P.LT.120.458D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.85.839D0) ISO_ERROR = 'E' ELSE IF (T.GT.325.0D0.AND.T.LE.350.0D0) THEN TIDX = 15 IF (P.LT.165.212D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.120.458D0) ISO_ERROR = 'E' ELSE IF (T.GT.350.0D0.AND.T.LE.400.0D0) THEN TIDX = 16 IF (P.GE.165.212D0.AND.P.LT.200.0D0) THEN PIDX = PIDX + 3 ISO_ERROR = 'W' ENDIF IF (P.GE.200.0D0.AND.P.LT.250.0D0) THEN PIDX = PIDX + 2 ISO_ERROR = 'W' ENDIF IF (P.GE.250.0D0.AND.P.LT.300.0D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.165.212D0) ISO_ERROR = 'E' ELSE IF (T.GT.400.0D0.AND.T.LE.410.0D0) THEN TIDX = 17 IF (P.GE.200.0D0.AND.P.LT.350.0D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.200.0D0) ISO_ERROR = 'E' ELSE IF (T.GT.410.0D0.AND.T.LE.420.0D0) THEN TIDX = 18 IF (P.GE.350.0D0.AND.P.LT.400.0D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.350.0D0) ISO_ERROR = 'E' ELSE IF (T.GT.420.0D0.AND.T.LE.430.0D0) THEN TIDX = 19 IF (P.GE.350.0D0.AND.P.LT.400.0D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.350.0D0) ISO_ERROR = 'E' ELSE IF (T.GT.430.0D0.AND.T.LE.440.0D0) THEN TIDX = 20 IF (P.GE.400.0D0.AND.P.LT.450.0D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.400.0D0) ISO_ERROR = 'E' ELSE IF (T.GT.440.0D0.AND.T.LE.450.0D0) THEN TIDX = 21 IF (P.GE.450.0D0.AND.P.LT.500.0D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.450.0D0) ISO_ERROR = 'E' ELSE IF (T.GT.450.0D0.AND.T.LE.460.0D0) THEN TIDX = 22 IF (P.GE.450.0D0.AND.P.LT.500.0D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.450.0D0) ISO_ERROR = 'E' ELSE IF (T.GT.460.0D0.AND.T.LE.470.0D0) THEN TIDX = 23 IF (P.GE.500.0D0.AND.P.LT.550.0D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.500.0D0) ISO_ERROR = 'E' ELSE IF (T.GT.470.0D0.AND.T.LE.480.0D0) THEN TIDX = 24 IF (P.GE.550.0D0.AND.P.LT.600.0D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.550.0D0) ISO_ERROR = 'E' ELSE IF (T.GT.480.0D0.AND.T.LE.490.0D0) THEN TIDX = 25 IF (P.GE.550.0D0.AND.P.LT.600.0D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.550.0D0) ISO_ERROR = 'E' ELSE IF (T.GT.490.0D0.AND.T.LE.500.0D0) THEN TIDX = 26 IF (P.GE.600.0D0.AND.P.LT.650.0D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.600.0D0) ISO_ERROR = 'E' ELSE IF (T.GT.500.0D0.AND.T.LE.510.0D0) THEN TIDX = 27 IF (P.GE.600.0D0.AND.P.LT.650.0D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.600.0D0) ISO_ERROR = 'E' ELSE IF (T.GT.510.0D0.AND.T.LE.520.0D0) THEN TIDX = 28 IF (P.GE.650.0D0.AND.P.LT.700.0D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.650.0D0) ISO_ERROR = 'E' ELSE IF (T.GT.520.0D0.AND.T.LE.530.0D0) THEN TIDX = 29 IF (P.GE.700.0D0.AND.P.LT.750.0D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.700.0D0) ISO_ERROR = 'E' ELSE IF (T.GT.530.0D0.AND.T.LE.540.0D0) THEN TIDX = 30 IF (P.GE.700.0D0.AND.P.LT.750.0D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.700.0D0) ISO_ERROR = 'E' ELSE IF (T.GT.540.0D0.AND.T.LE.550.0D0) THEN TIDX = 31 IF (P.GE.750.0D0.AND.P.LT.800.0D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.700.0D0) ISO_ERROR = 'E' ELSE IF (T.GT.550.0D0.AND.T.LE.560.0D0) THEN TIDX = 32 IF (P.GE.800.0D0.AND.P.LT.850.0D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.800.0D0) ISO_ERROR = 'E' ELSE IF (T.GT.560.0D0.AND.T.LE.570.0D0) THEN TIDX = 33 IF (P.GE.800.0D0.AND.P.LT.850.0D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.800.0D0) ISO_ERROR = 'E' ELSE IF (T.GT.570.0D0.AND.T.LE.580.0D0) THEN TIDX = 34 IF (P.GE.850.0D0.AND.P.LT.900.0D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.850.0D0) ISO_ERROR = 'E' ELSE IF (T.GT.580.0D0.AND.T.LE.590.0D0) THEN TIDX = 35 IF (P.GE.900.0D0.AND.P.LT.950.0D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.900.0D0) ISO_ERROR = 'E' ELSE IF (T.GT.590.0D0.AND.T.LE.600.0D0) THEN TIDX = 36 IF (P.GE.900.0D0.AND.P.LT.950.0D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.900.0D0) ISO_ERROR = 'E' ELSE IF (T.GT.600.0D0.AND.T.LE.700.0D0) THEN TIDX = 36 IF (PT_ERROR.EQ.'N') PT_ERROR = 'W' IF (P.GE.900.0D0.AND.P.LT.950.0D0) THEN PIDX = PIDX + 1 ISO_ERROR = 'W' ENDIF IF (P.LT.800.0D0) ISO_ERROR = 'E' ELSE IF (T.GT.700.0D0) THEN TIDX = 36 PT_ERROR = 'E' ENDIF PT_FOUND = .TRUE. RETURN END SUBROUTINE INTRP3D (P, T, PIDX, TIDX, LOGKARRAY, LOGK) *======================================================================| * Interpolates intermediate LogK(P,T) values from LogK(P,T) array * using specified P, T, and array indices PIDX and TIDX found using * subroutine PT_INDEXER * (Interpolates intermdiate f(x,y) values from f(x,y) array using * specified x, y, and array indices XIDX and YTIDX found using * subroutine XY_INDEXER) * Copyright 2023 James Palandri * James Palandri hereby disclaims all copyright interest in the * Subroutine "INTRP3D" (which interpolates intermdiate logK values * from known logK values that bracket the interpolated value). * written by James Palandri * Signature of James Palandri, 31 January 2024 * James Palandri * This file is part of subroutine INTRP3D. * INTRP3D is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. INTRP3D is distributed in * the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more * details. You should have received a copy of the GNU * General Public License along with SuprxnGT. If not, see * . *----------------------------------------------------------------------| IMPLICIT NONE INTEGER PDIM, TDIM, PIDX, TIDX, I, J, K, L PARAMETER (PDIM = 37) PARAMETER (TDIM = 36) REAL*8 P, T, PVAL(PDIM), TVAL(TDIM), LOGKARRAY (PDIM,TDIM) REAL*8 SUM, LK(3), LOGK DATA PVal / 1.0d0, 1.0133d0, 2.321d0, 4.758d0, 8.919d0, 15.537d0, + 25.479d0, 39.737d0, 59.432d0, 85.839d0, 120.458d0, + 165.212d0, 200.d0, 250.d0, 300.d0, 350.d0, 400.d0, + 450.d0, 500.d0, 550.d0, 600.d0, 650.d0, 700.d0, + 750.d0, 800.d0, 850.d0, 900.d0, 950.d0, + 1000.d0, 1500.d0, 2000.d0, 2500.d0, 3000.d0, 3500.d0, + 4000.d0, 4500.d0, 5000.d0 / DATA TVal / 0.01d0, 25.d0, 50.d0, 75.d0, 100.d0, 125.d0, 150.d0, + 175.d0, 200.d0, 225.d0, 250.d0, 275.d0, 300.d0, + 325.d0, 350.d0, 400.d0, 410.d0, 420.d0, 430.d0, + 440.d0, 450.d0, 460.d0, 470.d0, 480.d0, 490.d0, + 500.d0, 510.d0, 520.d0, 530.d0, 540.d0, 550.d0, + 560.d0, 570.d0, 580.d0, 590.d0, 600.d0 / *======================================================================| LK(1) = 0.0D0 LK(2) = 0.0D0 LK(3) = 0.0D0 LOGK = 0.0D0 * Interpolate three logk's at grid at the selected pressure, at * temperatures bracketing the selected temperature * Note that lowest TIDX value passed to this subroutine is 3 * and the lowest PIDX value passed is 1. L = 1 DO 500, I = TIDX - 2, TIDX DO 400, J = PIDX, PIDX + 2 SUM = LOGKARRAY(J,I) DO 300, K = PIDX, PIDX + 2 IF (J.NE.K) SUM = SUM*(P-PVAL(K))/(PVAL(J)-PVAL(K)) 300 CONTINUE LK(L) = LK(L) + SUM 400 CONTINUE L = L + 1 500 CONTINUE * Interpolate log k at the selected temperature K = 1 DO 700, I = TIDX - 2, TIDX SUM = LK(K) DO 600, J = TIDX - 2, TIDX IF (I.NE.J) SUM = SUM*(T-TVAL(J))/(TVAL(I)-TVAL(J)) 600 CONTINUE LOGK = LOGK + SUM K = K + 1 700 CONTINUE RETURN END SUBROUTINE INTRP2D (TEMPC, TWARN, MAXT, LGKDAT, LOGK) *======================================================================| * * Interpolates log K(s) at L-V saturation for non-Supcrt L-V data at * 25, 50, 100, 150, 200, 250, 300, 350 * * ---------------------------------------------------------------------| IMPLICIT NONE LOGICAL TWARN INTEGER I, IT, J, MAXTIX, IOUT1 REAL*8 TEMPC, LGKDAT(8), TDATA(8), LOGK, MAXT, SUM DATA TDATA + /25.D0, 50.D0, 100.D0, 150.D0, 200.D0, 250.D0, 300.D0, 350.D0/ *======================================================================| TWARN = .FALSE. **Max TEMPC value and array index for that value (TDATA(8) = 350 currently) IOUT1 = 11 MAXTIX = 8 MAXT = 350.D0 LOGK = 0.D0 IT = 0 **Find highest TEMPC for which data exist, and array index for that TEMPC ** log K = 99999.999 where data do not exist. DO 100, I = 8, 2, -1 IF (LGKDAT(I).EQ.99999.999D0) THEN MAXT = TDATA(I - 1) MAXTIX = I - 1 ENDIF 100 CONTINUE **Flag if Temperaute is outside the range for which log Ks exist in soltherm. IF (TEMPC.GT.TDATA(MAXTIX).OR.TEMPC.LT.TDATA(1)) TWARN = .TRUE. **If logk only at 25C, set logK = LGKDAT(1) IF (MAXTIX.EQ.1) THEN IF (TEMPC.LE.35.D0.AND.TEMPC.GE.10.D0) THEN LOGK = LGKDAT(1) ELSE LOGK = 99999.999D0 ENDIF **Else if logk only at 25 and 50C, inter/extrapolate w/ LGKDAT(1) and LGKDAT(2) ELSE IF (MAXTIX.EQ.2) THEN IF (TEMPC.GE.0.01D0.AND.TEMPC.LE.75.D0) THEN LOGK = LGKDAT(1) + (TEMPC - 25.D0)* + (LGKDAT(2)-LGKDAT(1))/(50.d0-25.D0) ELSE LOGK = 99999.999D0 ENDIF ELSE **Compute desired log K value for the given TEMPC-value (T)....... ** The following should work wherever a mineral has data to 100C or above. ** Find where the desired TEMPC value lies in the TEMPC data array. DO 150, I = 1,7 IF (TEMPC.GT.TDATA(I).AND.TEMPC.LE.TDATA(I+1)) IT = I 150 CONTINUE IF (TEMPC.GT.TDATA(8)) IT = 7 **If TEMPC .gt. the first TEMPC-array value that is 99999.999 or ** .gt. 400C, set logK to 99999.999. Max extrapolation is 50C. * IF (IT.GE.(MAXTIX+1).OR.TEMPC.GT.400.D0) THEN IF (TEMPC.GT.TDATA(MAXTIX)+50.D0.OR.TEMPC.GT.400.D0) THEN LOGK = 99999.999D0 ELSE IF (IT.LT.2) THEN *** If TEMPC .LT. the second TDATA value, use the 3 smallest TDATA values DO 300, I = 1, 3 SUM = LGKDAT(I) DO 200, J = 1,3 IF (I.NE.J) + SUM = SUM*(TEMPC-TDATA(J))/(TDATA(I)-TDATA(J)) 200 CONTINUE LOGK = LOGK + SUM 300 CONTINUE ELSE IF (IT.GT.MAXTIX-1) THEN **** If TEMPC .GT. next to last TDATA value, use 3 largest TDATA values DO 500, I = MAXTIX-2, MAXTIX SUM = LGKDAT(I) DO 400, J = MAXTIX-2, MAXTIX IF (I.NE.J) + SUM = SUM*(TEMPC-TDATA(J))/(TDATA(I)-TDATA(J)) 400 CONTINUE LOGK = LOGK + SUM 500 CONTINUE ELSE **** Else use TDATA values two below, one above TEMPC DO 700, I = IT-1, IT+1 SUM = LGKDAT(I) DO 600, J = IT-1, IT+1 IF (I.NE.J) + SUM = SUM*(TEMPC -TDATA(J))/(TDATA(I)-TDATA(J)) 600 CONTINUE LOGK = LOGK + SUM 700 CONTINUE ENDIF ENDIF RETURN END