MODULE co2calc 1,5 !----------------------------------------------------------------------------- ! based upon OCMIP2 co2calc ! ! CVS:$Id: co2calc.F90 941 2006-05-12 21:36:48Z klindsay $ ! CVS:$Name$ !----------------------------------------------------------------------------- USE constants USE blocks, ONLY : nx_block, ny_block USE domain_size, ONLY : max_blocks_clinic USE kinds_mod USE state_mod, ONLY : ref_pressure #ifdef CCSMCOUPLED !*** ccsm USE shr_vmath_mod #endif IMPLICIT NONE !----------------------------------------------------------------------------- ! public/private declarations !----------------------------------------------------------------------------- PRIVATE PUBLIC :: co2calc_row, comp_CO3terms, comp_co3_sat_vals !----------------------------------------------------------------------------- ! module parameters !----------------------------------------------------------------------------- !----------------------------------------------------------------------------- ! The current setting of xacc, a tolerance critera, will result in co2star ! being accurate to 3 significant figures (xx.y). Making xacc bigger will ! result in faster convergence also, but this is not recommended (xacc of ! 10**-9 drops precision to 2 significant figures). !----------------------------------------------------------------------------- REAL(KIND=r8), PARAMETER :: xacc = 1e-10_r8 INTEGER(KIND=int_kind), PARAMETER :: maxit = 100 !----------------------------------------------------------------------------- ! declarations for function coefficients & species concentrations !----------------------------------------------------------------------------- REAL(KIND=r8), DIMENSION(nx_block,max_blocks_clinic) :: & kw, kb, ks, kf, k1p, k2p, k3p, ksi, & bt, st, ft, dic, ta, pt, sit !***************************************************************************** CONTAINS !***************************************************************************** SUBROUTINE co2calc_row(iblock, mask, locmip_k1_k2_bug_fix, t, s, & 1,1 dic_in, ta_in, pt_in, sit_in, phlo, phhi, ph, xco2_in, atmpres, & co2star, dco2star, pCO2surf, dpco2) !--------------------------------------------------------------------------- ! SUBROUTINE co2calc_row ! ! PURPOSE : Calculate delta co2*, etc. from total alkalinity, total CO2, ! temp, salinity (s), etc. !--------------------------------------------------------------------------- !--------------------------------------------------------------------------- ! input arguments !--------------------------------------------------------------------------- INTEGER(KIND=int_kind), INTENT(IN) :: iblock LOGICAL(KIND=log_kind), DIMENSION(nx_block), INTENT(IN) :: mask LOGICAL(KIND=log_kind), INTENT(IN) :: locmip_k1_k2_bug_fix REAL(KIND=r8), DIMENSION(nx_block), INTENT(IN) :: & t, & ! temperature (degrees C) s, & ! salinity (PSU) dic_in, & ! total inorganic carbon (nmol/cm^3) ta_in, & ! total alkalinity (neq/cm^3) pt_in, & ! inorganic phosphate (nmol/cm^3) sit_in, & ! inorganic silicate (nmol/cm^3) phlo, & ! lower limit of pH range phhi, & ! upper limit of pH range xco2_in, & ! atmospheric mole fraction CO2 in dry air (ppmv) atmpres ! atmospheric pressure (atmosphere) !--------------------------------------------------------------------------- ! output arguments !--------------------------------------------------------------------------- REAL(KIND=r8), DIMENSION(nx_block), INTENT(OUT) :: & ph, & ! computed ph values, for initial guess on next time step co2star, & ! CO2*water (nmol/cm^3) dco2star, & ! delta CO2 (nmol/cm^3) pco2surf, & ! oceanic pCO2 (ppmv) dpco2 ! Delta pCO2, i.e, pCO2ocn - pCO2atm (ppmv) !--------------------------------------------------------------------------- ! local variable declarations !--------------------------------------------------------------------------- INTEGER(KIND=int_kind) :: i INTEGER(KIND=int_kind) :: k REAL(KIND=r8) :: & mass_to_vol, & ! (mol/kg) -> (mmol/m^3) vol_to_mass, & ! (mmol/m^3) -> (mol/kg) co2starair, & ! co2star saturation htotal2 REAL(KIND=r8), DIMENSION(nx_block) :: & xco2, & ! atmospheric CO2 (atm) htotal, & ! free concentration of H ion k0,k1,k2, & ! equilibrium constants for CO2 species ff ! fugacity of CO2 !--------------------------------------------------------------------------- ! check for existence of ocean points !--------------------------------------------------------------------------- IF (COUNT(mask) == 0) THEN ph = c0 co2star = c0 dco2star = c0 pCO2surf = c0 dpCO2 = c0 RETURN END IF !--------------------------------------------------------------------------- ! set unit conversion factors !--------------------------------------------------------------------------- mass_to_vol = 1e6_r8 * rho_sw vol_to_mass = c1 / mass_to_vol !--------------------------------------------------------------------------- ! convert tracer units to per mass & xco2 from uatm to atm !--------------------------------------------------------------------------- DO i = 1,nx_block IF (mask(i)) THEN dic(i,iblock) = dic_in(i) * vol_to_mass xco2(i) = xco2_in(i) * 1e-6_r8 END IF ! if mask END DO ! i loop !--------------------------------------------------------------------------- ! compute htotal !--------------------------------------------------------------------------- k = 1 CALL comp_htotal_kvals(iblock, k, mask, t, s, dic_in, ta_in, pt_in, sit_in, & phlo, phhi, htotal, k0, k1, k2, ff, & k1_k2_pH_tot=locmip_k1_k2_bug_fix) !--------------------------------------------------------------------------- ! Compute co2starair !--------------------------------------------------------------------------- !--------------------------------------------------------------------------- ! Calculate [CO2*] as defined in DOE Methods Handbook 1994 Ver.2, ! ORNL/CDIAC-74, Dickson and Goyet, eds. (Ch 2 p 10, Eq A.49) !--------------------------------------------------------------------------- DO i = 1,nx_block IF (mask(i)) THEN htotal2 = htotal(i) ** 2 co2star(i) = dic(i,iblock) * htotal2 / & (htotal2 + k1(i) * htotal(i) + k1(i) * k2(i)) co2starair = xco2(i) * ff(i) * atmpres(i) dco2star(i) = co2starair - co2star(i) ph(i) = -LOG10(htotal(i)) !--------------------------------------------------------------------- ! Add two output arguments for storing pCO2surf ! Should we be using K0 or ff for the solubility here? !--------------------------------------------------------------------- pCO2surf(i) = co2star(i) / ff(i) dpCO2(i) = pCO2surf(i) - xco2(i) * atmpres(i) !--------------------------------------------------------------------- ! Convert units of output arguments ! Note: pCO2surf and dpCO2 are calculated in atm above. !--------------------------------------------------------------------- co2star(i) = co2star(i) * mass_to_vol dco2star(i) = dco2star(i) * mass_to_vol pCO2surf(i) = pCO2surf(i) * 1e6_r8 dpCO2(i) = dpCO2(i) * 1e6_r8 ELSE ! if mask ph(i) = c0 co2star(i) = c0 dco2star(i) = c0 pCO2surf(i) = c0 dpCO2(i) = c0 END IF ! if mask END DO ! i loop END SUBROUTINE co2calc_row !***************************************************************************** SUBROUTINE comp_CO3terms(iblock, k, mask, t, s, dic_in, ta_in, pt_in, sit_in, & 1,1 phlo, phhi, ph, H2CO3, HCO3, CO3) !--------------------------------------------------------------------------- ! SUBROUTINE comp_CO3terms ! ! PURPOSE : Calculate H2CO3, HCO3, CO3 from ! total alkalinity, total CO2, temp, salinity (s), etc. !--------------------------------------------------------------------------- !--------------------------------------------------------------------------- ! input arguments !--------------------------------------------------------------------------- INTEGER(KIND=int_kind), INTENT(IN) :: iblock INTEGER(KIND=int_kind), INTENT(IN) :: k LOGICAL(KIND=log_kind), DIMENSION(nx_block, ny_block), INTENT(IN) :: mask REAL(KIND=r8), DIMENSION(nx_block,ny_block), INTENT(IN) :: & t, & ! temperature (degrees C) s, & ! salinity (PSU) dic_in, & ! total inorganic carbon (nmol/cm^3) ta_in, & ! total alkalinity (neq/cm^3) pt_in, & ! inorganic phosphate (nmol/cm^3) sit_in, & ! inorganic silicate (nmol/cm^3) phlo, & ! lower limit of pH range phhi ! upper limit of pH range !--------------------------------------------------------------------------- ! output arguments !--------------------------------------------------------------------------- REAL(KIND=r8), DIMENSION(nx_block, ny_block), INTENT(OUT) :: & pH, & ! computed ph values, for initial guess on next time step H2CO3, & ! Carbonic Acid Concentration HCO3, & ! Bicarbonate Ion Concentration CO3 ! Carbonate Ion Concentration !--------------------------------------------------------------------------- ! local variable declarations !--------------------------------------------------------------------------- INTEGER(KIND=int_kind) :: i, j REAL(KIND=r8) :: & mass_to_vol, & ! (mol/kg) -> (mmol/m^3) vol_to_mass, & ! (mmol/m^3) -> (mol/kg) htotal2, denom REAL(KIND=r8), DIMENSION(nx_block) :: & htotal, & ! free concentration of H ion k0,k1,k2, & ! equilibrium constants for CO2 species ff ! fugacity of CO2 !--------------------------------------------------------------------------- ! check for existence of ocean points !--------------------------------------------------------------------------- IF (COUNT(mask) == 0) THEN ph = c0 H2CO3 = c0 HCO3 = c0 CO3 = c0 RETURN END IF !--------------------------------------------------------------------------- ! set unit conversion factors !--------------------------------------------------------------------------- mass_to_vol = 1e6_r8 * rho_sw vol_to_mass = c1 / mass_to_vol DO j = 1,ny_block !------------------------------------------------------------------------ ! check for existence of ocean points on this row !------------------------------------------------------------------------ IF (COUNT(mask(:,j)) == 0) THEN ph(:,j) = c0 H2CO3(:,j) = c0 HCO3(:,j) = c0 CO3(:,j) = c0 CYCLE END IF !------------------------------------------------------------------------ ! convert tracer units to per mass !------------------------------------------------------------------------ DO i = 1,nx_block IF (mask(i,j)) THEN dic(i,iblock) = dic_in(i,j) * vol_to_mass END IF ! if mask END DO ! i loop !------------------------------------------------------------------------ ! compute htotal !------------------------------------------------------------------------ CALL comp_htotal_kvals(iblock, k, mask(:,j), t(:,j), s(:,j), dic_in(:,j), & ta_in(:,j), pt_in(:,j), sit_in(:,j), & phlo(:,j), phhi(:,j), htotal, k0, k1, k2, ff) !------------------------------------------------------------------------ ! Calculate [CO2*] as defined in DOE Methods Handbook 1994 Ver.2, ! ORNL/CDIAC-74, Dickson and Goyet, eds. (Ch 2 p 10, Eq A.49-51) !------------------------------------------------------------------------ DO i = 1,nx_block IF (mask(i,j)) THEN htotal2 = htotal(i) ** 2 denom = c1 / (htotal2 + k1(i) * htotal(i) + k1(i) * k2(i)) H2CO3(i,j) = dic(i,iblock) * htotal2 * denom HCO3(i,j) = dic(i,iblock) * k1(i) * htotal(i) * denom CO3(i,j) = dic(i,iblock) * k1(i) * k2(i) * denom ph(i,j) = -LOG10(htotal(i)) !------------------------------------------------------------------ ! Convert units of output arguments !------------------------------------------------------------------ H2CO3(i,j) = H2CO3(i,j) * mass_to_vol HCO3(i,j) = HCO3(i,j) * mass_to_vol CO3(i,j) = CO3(i,j) * mass_to_vol ELSE ! if mask ph(i,j) = c0 H2CO3(i,j) = c0 HCO3(i,j) = c0 CO3(i,j) = c0 END IF ! if mask END DO ! i loop END DO ! j loop END SUBROUTINE comp_CO3terms !***************************************************************************** SUBROUTINE comp_co3_sat_vals(k, mask, t, s, co3_sat_calc, co3_sat_arag) 1,7 !--------------------------------------------------------------------------- ! SUBROUTINE comp_co3_sat_vals ! ! PURPOSE : Calculate co3 concentration at calcite and aragonite saturation ! from temp, salinity (s), press !--------------------------------------------------------------------------- !--------------------------------------------------------------------------- ! input arguments !--------------------------------------------------------------------------- INTEGER(KIND=int_kind), INTENT(IN) :: k LOGICAL(KIND=log_kind), DIMENSION(nx_block, ny_block), INTENT(IN) :: mask REAL(KIND=r8), DIMENSION(nx_block,ny_block), INTENT(IN) :: & t, & ! temperature (degrees C) s ! salinity (PSU) !--------------------------------------------------------------------------- ! output arguments !--------------------------------------------------------------------------- REAL(KIND=r8), DIMENSION(nx_block, ny_block), INTENT(OUT) :: & co3_sat_calc,&! co3 concentration at calcite saturation co3_sat_arag ! co3 concentration at aragonite saturation !--------------------------------------------------------------------------- ! local variable declarations !--------------------------------------------------------------------------- INTEGER(KIND=int_kind) :: i, j REAL(KIND=r8) :: & mass_to_vol, & ! (mol/kg) -> (mmol/m^3) press_bar ! pressure at level k [bars] REAL(KIND=r8), DIMENSION(nx_block) :: & tk, & ! temperature (K) log10tk,invtk,sqrts,s15,invRtk,arg,& K_calc, & ! thermodynamic constant for calcite K_arag, & ! thermodynamic constant for aragonite deltaV,Kappa, & ! pressure correction terms lnKfac,Kfac, & ! pressure correction terms inv_Ca ! inverse of Calcium concentration (mol/kg) !--------------------------------------------------------------------------- ! check for existence of ocean points !--------------------------------------------------------------------------- IF (COUNT(mask) == 0) THEN co3_sat_calc = c0 co3_sat_arag = c0 RETURN END IF !--------------------------------------------------------------------------- ! set unit conversion factors !--------------------------------------------------------------------------- mass_to_vol = 1e6_r8 * rho_sw press_bar = ref_pressure(k) DO j = 1,ny_block !------------------------------------------------------------------------ ! check for existence of ocean points on this row !------------------------------------------------------------------------ IF (COUNT(mask(:,j)) == 0) THEN co3_sat_calc(:,j) = c0 co3_sat_arag(:,j) = c0 CYCLE END IF tk = T0_Kelvin + t(:,j) #ifdef CCSMCOUPLED CALL shr_vmath_log(tk, log10tk, nx_block) #else log10tk = LOG(tk) #endif log10tk = log10tk/LOG(c10) invtk = c1 / tk invRtk = (c1 / 83.1451_r8) * invtk #ifdef CCSMCOUPLED CALL shr_vmath_sqrt(s(:,j), sqrts, nx_block) #else sqrts = SQRT(s(:,j)) #endif s15 = sqrts * s(:,j) !------------------------------------------------------------------------ ! Compute K_calc, K_arag, apply pressure factor ! Mucci, Amer. J. of Science 283:781-799, 1983 & Millero 1979 !------------------------------------------------------------------------ arg = -171.9065_r8 - 0.077993_r8 * tk + 2839.319_r8 * invtk + 71.595_r8 * log10tk + & (-0.77712_r8 + 0.0028426_r8 * tk + 178.34_r8 * invtk) * sqrts - & 0.07711_r8 * s(:,j) + 0.0041249_r8 * s15 arg = LOG(c10) * arg #ifdef CCSMCOUPLED CALL shr_vmath_exp(arg, K_calc, nx_block) #else K_calc = EXP(arg) #endif IF (k > 1) THEN deltaV = -48.76_r8 + 0.5304_r8 * t(:,j) Kappa = (-11.76_r8 + 0.3692_r8 * t(:,j)) * p001 lnKfac = (-deltaV + p5 * Kappa * press_bar) * press_bar * invRtk #ifdef CCSMCOUPLED CALL shr_vmath_exp(lnKfac, Kfac, nx_block) #else Kfac = EXP(lnKfac) #endif K_calc = K_calc * Kfac END IF arg = -171.945_r8 - 0.077993_r8 * tk + 2903.293_r8 * invtk + 71.595_r8 * log10tk + & (-0.068393_r8 + 0.0017276_r8 * tk + 88.135_r8 * invtk) * sqrts - & 0.10018_r8 * s(:,j) + 0.0059415_r8 * s15 arg = LOG(c10) * arg #ifdef CCSMCOUPLED CALL shr_vmath_exp(arg, K_arag, nx_block) #else K_arag = EXP(arg) #endif IF (k > 1) THEN deltaV = deltaV + 2.8_r8 lnKfac = (-deltaV + p5 * Kappa * press_bar) * press_bar * invRtk #ifdef CCSMCOUPLED CALL shr_vmath_exp(lnKfac, Kfac, nx_block) #else Kfac = EXP(lnKfac) #endif K_arag = K_arag * Kfac END IF WHERE ( mask(:,j) ) !------------------------------------------------------------------ ! Compute CO3 concentration at calcite & aragonite saturation !------------------------------------------------------------------ inv_Ca = (35.0_r8 / 0.01028_r8) / s(:,j) co3_sat_calc(:,j) = K_calc * inv_Ca co3_sat_arag(:,j) = K_arag * inv_Ca !------------------------------------------------------------------ ! Convert units of output arguments !------------------------------------------------------------------ co3_sat_calc(:,j) = co3_sat_calc(:,j) * mass_to_vol co3_sat_arag(:,j) = co3_sat_arag(:,j) * mass_to_vol ELSEWHERE co3_sat_calc(:,j) = c0 co3_sat_arag(:,j) = c0 END WHERE END DO ! j loop END SUBROUTINE comp_co3_sat_vals !***************************************************************************** SUBROUTINE comp_htotal_kvals(iblock, k, mask, t, s, dic_in, ta_in, pt_in, sit_in, & 2,2 phlo, phhi, htotal, k0, k1, k2, ff, k1_k2_pH_tot) !--------------------------------------------------------------------------- ! SUBROUTINE comp_htotal_kvals ! ! PURPOSE : Calculate htotal, k0, k1, k2, ff from total alkalinity, total CO2, ! temp, salinity (s), etc. !--------------------------------------------------------------------------- !--------------------------------------------------------------------------- ! input arguments !--------------------------------------------------------------------------- INTEGER(KIND=int_kind), INTENT(IN) :: iblock INTEGER(KIND=int_kind), INTENT(IN) :: k LOGICAL(KIND=log_kind), DIMENSION(nx_block), INTENT(IN) :: mask REAL(KIND=r8), DIMENSION(nx_block), INTENT(IN) :: & t, & ! temperature (degrees C) s, & ! salinity (PSU) dic_in, & ! total inorganic carbon (nmol/cm^3) ta_in, & ! total alkalinity (neq/cm^3) pt_in, & ! inorganic phosphate (nmol/cm^3) sit_in, & ! inorganic silicate (nmol/cm^3) phlo, & ! lower limit of pH range phhi ! upper limit of pH range LOGICAL(KIND=log_kind), INTENT(IN), OPTIONAL :: k1_k2_pH_tot !--------------------------------------------------------------------------- ! output arguments !--------------------------------------------------------------------------- REAL(KIND=r8), DIMENSION(nx_block), INTENT(OUT) :: & htotal, & ! free concentration of H ion k0,k1,k2, & ! equilibrium constants for CO2 species ff ! fugacity of CO2 !--------------------------------------------------------------------------- ! local variable declarations !--------------------------------------------------------------------------- INTEGER(KIND=int_kind) :: i REAL(KIND=r8) :: & mass_to_vol, & ! (mol/kg) -> (mmol/m^3) vol_to_mass ! (mmol/m^3) -> (mol/kg) REAL(KIND=r8), DIMENSION(nx_block) :: & x1, x2 ! bounds on htotal for solver LOGICAL(KIND=log_kind) :: k1_k2_pH_tot_loc !--------------------------------------------------------------------------- ! check for existence of ocean points !--------------------------------------------------------------------------- IF (COUNT(mask) == 0) THEN htotal = c0 RETURN END IF !--------------------------------------------------------------------------- ! set unit conversion factors !--------------------------------------------------------------------------- mass_to_vol = 1e6_r8 * rho_sw vol_to_mass = c1 / mass_to_vol !--------------------------------------------------------------------------- ! which pH scale is to be used for k1 & k2 constants !--------------------------------------------------------------------------- IF (PRESENT(k1_k2_pH_tot)) THEN k1_k2_pH_tot_loc = k1_k2_pH_tot ELSE k1_k2_pH_tot_loc = .true. END IF !--------------------------------------------------------------------------- ! convert tracer units to per mass !--------------------------------------------------------------------------- DO i = 1,nx_block IF (mask(i)) THEN dic(i,iblock) = dic_in(i) * vol_to_mass ta(i,iblock) = ta_in(i) * vol_to_mass pt(i,iblock) = max(pt_in(i),c0) * vol_to_mass sit(i,iblock) = max(sit_in(i),c0) * vol_to_mass x1(i) = c10 ** (-phhi(i)) x2(i) = c10 ** (-phlo(i)) END IF ! if mask END DO ! i loop CALL comp_co3_coeffs(iblock, k, mask, t, s, k0, k1, k2, ff, k1_k2_pH_tot_loc) !--------------------------------------------------------------------------- ! If DIC and TA are known then either a root finding or iterative ! method must be used to calculate htotal. In this case we use ! the Newton-Raphson "safe" method taken from "Numerical Recipes" ! (function "rtsafe.f" with error trapping removed). ! ! As currently set, this procedure iterates about 12 times. The ! x1 and x2 values set below will accomodate ANY oceanographic ! values. If an initial guess of the pH is known, then the ! number of iterations can be reduced to about 5 by narrowing ! the gap between x1 and x2. It is recommended that the first ! few time steps be run with x1 and x2 set as below. After that, ! set x1 and x2 to the previous value of the pH +/- ~0.5. !--------------------------------------------------------------------------- CALL drtsafe_row(iblock, mask, k1, k2, x1, x2, xacc, htotal) END SUBROUTINE comp_htotal_kvals !***************************************************************************** SUBROUTINE comp_co3_coeffs(iblock, k, mask, t, s, k0, k1, k2, ff, k1_k2_pH_tot) 1,28 !--------------------------------------------------------------------------- ! input arguments !--------------------------------------------------------------------------- INTEGER(KIND=int_kind), INTENT(IN) :: iblock INTEGER(KIND=int_kind), INTENT(IN) :: k LOGICAL(KIND=log_kind), DIMENSION(nx_block), INTENT(IN) :: mask REAL(KIND=r8), DIMENSION(nx_block), INTENT(IN) :: & t, & ! temperature (degrees C) s ! salinity (PSU) LOGICAL(KIND=log_kind), INTENT(IN) :: k1_k2_pH_tot !--------------------------------------------------------------------------- ! output arguments !--------------------------------------------------------------------------- REAL(KIND=r8), DIMENSION(nx_block), INTENT(OUT) :: & k0,k1,k2, & ! equilibrium constants for CO2 species ff ! fugacity of CO2 !--------------------------------------------------------------------------- ! local variable declarations !--------------------------------------------------------------------------- INTEGER(KIND=int_kind) :: i REAL(KIND=r8) :: & press_bar ! pressure at level k [bars] REAL(KIND=r8), DIMENSION(nx_block) :: & tk, & ! temperature (K) is, & ! ionic strength scl, & ! chlorinity tk100, tk1002, invtk, dlogtk, is2, sqrtis, & s2, sqrts, s15, invRtk, arg, & deltaV,Kappa,lnKfac,Kfac, & ! pressure correction terms log_1_m_1p005em3_s, & log_1_p_tot_sulfate_div_ks !--------------------------------------------------------------------------- press_bar = ref_pressure(k) !--------------------------------------------------------------------------- ! Calculate all constants needed to convert between various ! measured carbon species. References for each equation are ! noted in the code. Once calculated, the constants are stored ! and passed in the common block "const". The original version ! of this code was based on the code by Dickson in Version 2 of ! "Handbook of Methods for the Analysis of the Various Parameters ! of the Carbon Dioxide System in Seawater", DOE, 1994 (SOP No. 3, ! p25-26). ! Derive simple terms used more than once !--------------------------------------------------------------------------- tk = T0_Kelvin + t tk100 = tk * 1e-2_r8 tk1002 = tk100 * tk100 invtk = c1 / tk #ifdef CCSMCOUPLED CALL shr_vmath_log(tk, dlogtk, nx_block) #else dlogtk = LOG(tk) #endif invRtk = (c1 / 83.1451_r8) * invtk is = 19.924_r8 * s / (c1000 - 1.005_r8 * s) is2 = is * is #ifdef CCSMCOUPLED CALL shr_vmath_sqrt(is, sqrtis, nx_block) CALL shr_vmath_sqrt(s, sqrts, nx_block) #else sqrtis = SQRT(is) sqrts = SQRT(s) #endif s2 = s * s scl = s / 1.80655_r8 arg = c1 - 0.001005_r8 * s #ifdef CCSMCOUPLED CALL shr_vmath_log(arg, log_1_m_1p005em3_s, nx_block) #else log_1_m_1p005em3_s = LOG(arg) #endif !--------------------------------------------------------------------------- ! f = k0(1-pH2O)*correction term for non-ideality ! Weiss & Price (1980, Mar. Chem., 8, 347-359; ! Eq 13 with table 6 values) !--------------------------------------------------------------------------- arg = -162.8301_r8 + 218.2968_r8 / tk100 + & 90.9241_r8 * (dlogtk + LOG(1e-2_r8)) - 1.47696_r8 * tk1002 + & s * (.025695_r8 - .025225_r8 * tk100 + 0.0049867_r8 * tk1002) #ifdef CCSMCOUPLED CALL shr_vmath_exp(arg, ff, nx_block) #else ff = EXP(arg) #endif !--------------------------------------------------------------------------- ! K0 from Weiss 1974 !--------------------------------------------------------------------------- arg = 93.4517_r8 / tk100 - 60.2409_r8 + 23.3585_r8 * (dlogtk + LOG(1e-2_r8)) + & s * (.023517_r8 - 0.023656_r8 * tk100 + 0.0047036_r8 * tk1002) #ifdef CCSMCOUPLED CALL shr_vmath_exp(arg, k0, nx_block) #else k0 = EXP(arg) #endif !--------------------------------------------------------------------------- ! k1 = [H][HCO3]/[H2CO3] ! k2 = [H][CO3]/[HCO3] ! if k1_k2_pH_tot == .true., then use ! Lueker, Dickson, Keeling (2000) using Mehrbach et al. data on total scale ! otherwise, use ! Millero p.664 (1995) using Mehrbach et al. data on seawater scale ! this is only present to be consistent w/ OCMIP2 code ! it should not be used for new runs ! the only reason to use it is to be compatible with prior ! long spun up runs that had used it ! pressure correction from Millero 1995, p. 675 ! w/ typo corrections from CO2SYS !--------------------------------------------------------------------------- IF (k1_k2_pH_tot) THEN ! total pH scale arg = 3633.86_r8 * invtk - 61.2172_r8 + & 9.67770_r8 * dlogtk - 0.011555_r8 * s + & 0.0001152_r8 * s2 ELSE ! seawater pH scale, see comment above arg = 3670.7_r8 * invtk - 62.008_r8 + & 9.7944_r8 * dlogtk - 0.0118_r8 * s + & 0.000116_r8 * s2 END IF arg = -LOG(c10) * arg #ifdef CCSMCOUPLED CALL shr_vmath_exp(arg, k1, nx_block) #else k1 = EXP(arg) #endif IF (k > 1) THEN deltaV = -25.5_r8 + 0.1271_r8 * t Kappa = (-3.08_r8 + 0.0877_r8 * t) * p001 lnKfac = (-deltaV + p5 * Kappa * press_bar) * press_bar * invRtk #ifdef CCSMCOUPLED CALL shr_vmath_exp(lnKfac, Kfac, nx_block) #else Kfac = EXP(lnKfac) #endif k1 = k1 * Kfac END IF IF (k1_k2_pH_tot) THEN ! total pH scale arg = 471.78_r8 * invtk + 25.9290_r8 - & 3.16967_r8 * dlogtk - 0.01781_r8 * s + 0.0001122_r8 * s2 ELSE ! seawater pH scale, see comment above arg = 1394.7_r8 * invtk + 4.777_r8 - & 0.0184_r8 * s + 0.000118_r8 * s2 END IF arg = -LOG(c10) * arg #ifdef CCSMCOUPLED CALL shr_vmath_exp(arg, k2, nx_block) #else k2 = EXP(arg) #endif IF (k > 1) THEN deltaV = -15.82_r8 - 0.0219_r8 * t Kappa = (1.13_r8 - 0.1475_r8 * t) * p001 lnKfac = (-deltaV + p5 * Kappa * press_bar) * press_bar * invRtk #ifdef CCSMCOUPLED CALL shr_vmath_exp(lnKfac, Kfac, nx_block) #else Kfac = EXP(lnKfac) #endif k2 = k2 * Kfac END IF !--------------------------------------------------------------------------- ! kb = [H][BO2]/[HBO2] ! Millero p.669 (1995) using data from Dickson (1990) ! CO2SYS states that this in on total pH scale ! pressure correction from Millero 1979, p. 1657 ! omitting salinity contribution !--------------------------------------------------------------------------- arg = (-8966.90_r8 - 2890.53_r8 * sqrts - & 77.942_r8 * s + 1.728_r8 * s * sqrts - & 0.0996_r8 * s2) * invtk + & (148.0248_r8 + 137.1942_r8 * sqrts + 1.62142_r8 * s) + & (-24.4344_r8 - 25.085_r8 * sqrts - 0.2474_r8 * s) * dlogtk + & 0.053105_r8 * sqrts * tk #ifdef CCSMCOUPLED CALL shr_vmath_exp(arg, kb(:,iblock), nx_block) #else kb(:,iblock) = EXP(arg) #endif IF (k > 1) THEN deltaV = -29.48_r8 + (0.1622_r8 - 0.002608_r8 * t) * t Kappa = -2.84_r8 * p001 lnKfac = (-deltaV + p5 * Kappa * press_bar) * press_bar * invRtk #ifdef CCSMCOUPLED CALL shr_vmath_exp(lnKfac, Kfac, nx_block) #else Kfac = EXP(lnKfac) #endif kb(:,iblock) = kb(:,iblock) * Kfac END IF !--------------------------------------------------------------------------- ! k1p = [H][H2PO4]/[H3PO4] ! DOE(1994) eq 7.2.20 with footnote using data from Millero (1974) ! pressure correction from Millero 1995, p. 675 ! w/ typo corrections from CO2SYS !--------------------------------------------------------------------------- arg = -4576.752_r8 * invtk + 115.525_r8 - & 18.453_r8 * dlogtk + & (-106.736_r8 * invtk + 0.69171_r8) * sqrts + & (-0.65643_r8 * invtk - 0.01844_r8) * s #ifdef CCSMCOUPLED CALL shr_vmath_exp(arg, k1p(:,iblock), nx_block) #else k1p(:,iblock) = EXP(arg) #endif IF (k > 1) THEN deltaV = -14.51_r8 + (0.1211_r8 - 0.000321_r8 * t) * t Kappa = (-2.67_r8 + 0.0427_r8 * t) * p001 lnKfac = (-deltaV + p5 * Kappa * press_bar) * press_bar * invRtk #ifdef CCSMCOUPLED CALL shr_vmath_exp(lnKfac, Kfac, nx_block) #else Kfac = EXP(lnKfac) #endif k1p(:,iblock) = k1p(:,iblock) * Kfac END IF !--------------------------------------------------------------------------- ! k2p = [H][HPO4]/[H2PO4] ! DOE(1994) eq 7.2.23 with footnote using data from Millero (1974)) ! pressure correction from Millero 1995, p. 675 ! w/ typo corrections from CO2SYS !--------------------------------------------------------------------------- arg = -8814.715_r8 * invtk + 172.0883_r8 - & 27.927_r8 * dlogtk + & (-160.340_r8 * invtk + 1.3566_r8) * sqrts + & (0.37335_r8 * invtk - 0.05778_r8) * s #ifdef CCSMCOUPLED CALL shr_vmath_exp(arg, k2p(:,iblock), nx_block) #else k2p(:,iblock) = EXP(arg) #endif IF (k > 1) THEN deltaV = -23.12_r8 + (0.1758_r8 - 0.002647_r8 * t) * t Kappa = (-5.15_r8 + 0.09_r8 * t) * p001 lnKfac = (-deltaV + p5 * Kappa * press_bar) * press_bar * invRtk #ifdef CCSMCOUPLED CALL shr_vmath_exp(lnKfac, Kfac, nx_block) #else Kfac = EXP(lnKfac) #endif k2p(:,iblock) = k2p(:,iblock) * Kfac END IF !--------------------------------------------------------------------------- ! k3p = [H][PO4]/[HPO4] ! DOE(1994) eq 7.2.26 with footnote using data from Millero (1974) ! pressure correction from Millero 1995, p. 675 ! w/ typo corrections from CO2SYS !--------------------------------------------------------------------------- arg = -3070.75_r8 * invtk - 18.141_r8 + & (17.27039_r8 * invtk + 2.81197_r8) * sqrts + & (-44.99486_r8 * invtk - 0.09984_r8) * s #ifdef CCSMCOUPLED CALL shr_vmath_exp(arg, k3p(:,iblock), nx_block) #else k3p(:,iblock) = EXP(arg) #endif IF (k > 1) THEN deltaV = -26.57_r8 + (0.202_r8 - 0.003042_r8 * t) * t Kappa = (-4.08_r8 + 0.0714_r8 * t) * p001 lnKfac = (-deltaV + p5 * Kappa * press_bar) * press_bar * invRtk #ifdef CCSMCOUPLED CALL shr_vmath_exp(lnKfac, Kfac, nx_block) #else Kfac = EXP(lnKfac) #endif k3p(:,iblock) = k3p(:,iblock) * Kfac END IF !--------------------------------------------------------------------------- ! ksi = [H][SiO(OH)3]/[Si(OH)4] ! Millero p.671 (1995) using data from Yao and Millero (1995) ! pressure correction from Millero 1995, p. 675 ! w/ typo corrections from CO2SYS ! apply boric acid values !--------------------------------------------------------------------------- arg = -8904.2_r8 * invtk + 117.385_r8 - & 19.334_r8 * dlogtk + & (-458.79_r8 * invtk + 3.5913_r8) * sqrtis + & (188.74_r8 * invtk - 1.5998_r8) * is + & (-12.1652_r8 * invtk + 0.07871_r8) * is2 + & log_1_m_1p005em3_s #ifdef CCSMCOUPLED CALL shr_vmath_exp(arg, ksi(:,iblock), nx_block) #else ksi(:,iblock) = EXP(arg) #endif IF (k > 1) THEN deltaV = -29.48_r8 + (0.1622_r8 - 0.002608_r8 * t) * t Kappa = -2.84_r8 * p001 lnKfac = (-deltaV + p5 * Kappa * press_bar) * press_bar * invRtk #ifdef CCSMCOUPLED CALL shr_vmath_exp(lnKfac, Kfac, nx_block) #else Kfac = EXP(lnKfac) #endif ksi(:,iblock) = ksi(:,iblock) * Kfac END IF !--------------------------------------------------------------------------- ! kw = [H][OH] ! Millero p.670 (1995) using composite data ! following DOE Handbook, 0.015 substracted from constant to ! approximately convert from SWS pH scale to total pH scale ! pressure correction from Millero 1983 ! note that deltaV coeffs in Millero 1995 are those actually ! freshwater deltaV coeffs from Millero 1983 !--------------------------------------------------------------------------- arg = -13847.26_r8 * invtk + 148.9652_r8 - 23.6521_r8 * dlogtk + & (118.67_r8 * invtk - 5.977_r8 + 1.0495_r8 * dlogtk) * sqrts - & 0.01615_r8 * s #ifdef CCSMCOUPLED CALL shr_vmath_exp(arg, kw(:,iblock), nx_block) #else kw = EXP(arg) #endif IF (k > 1) THEN deltaV = -20.02_r8 + (0.1119_r8 - 0.001409_r8 * t) * t Kappa = (-5.13_r8 + 0.0794_r8 * t) * p001 lnKfac = (-deltaV + p5 * Kappa * press_bar) * press_bar * invRtk #ifdef CCSMCOUPLED CALL shr_vmath_exp(lnKfac, Kfac, nx_block) #else Kfac = EXP(lnKfac) #endif kw(:,iblock) = kw(:,iblock) * Kfac END IF !--------------------------------------------------------------------------- ! ks = [H][SO4]/[HSO4], free pH scale ! Dickson (1990, J. chem. Thermodynamics 22, 113) ! pressure correction from Millero 1995, p. 675 ! w/ typo corrections from CO2SYS !--------------------------------------------------------------------------- arg = -4276.1_r8 * invtk + 141.328_r8 - 23.093_r8 * dlogtk + & (-13856.0_r8 * invtk + 324.57_r8 - 47.986_r8 * dlogtk) * sqrtis + & (35474.0_r8 * invtk - 771.54_r8 + 114.723_r8 * dlogtk) * is - & 2698.0_r8 * invtk * is * sqrtis + & 1776.0_r8 * invtk * is2 + & log_1_m_1p005em3_s #ifdef CCSMCOUPLED CALL shr_vmath_exp(arg, ks(:,iblock), nx_block) #else ks(:,iblock) = EXP(arg) #endif IF (k > 1) THEN deltaV = -18.03_r8 + (0.0466_r8 + 0.000316_r8 * t) * t Kappa = (-4.53_r8 + 0.09_r8 * t) * p001 lnKfac = (-deltaV + p5 * Kappa * press_bar) * press_bar * invRtk #ifdef CCSMCOUPLED CALL shr_vmath_exp(lnKfac, Kfac, nx_block) #else Kfac = EXP(lnKfac) #endif ks(:,iblock) = ks(:,iblock) * Kfac END IF !--------------------------------------------------------------------- ! kf = [H][F]/[HF] ! Dickson and Riley (1979) -- change pH scale to total ! pressure correction from Millero 1995, p. 675 ! w/ typo corrections from CO2SYS !--------------------------------------------------------------------- arg = c1 + (0.1400_r8 / 96.062_r8) * (scl) / ks(:,iblock) #ifdef CCSMCOUPLED CALL shr_vmath_log(arg, log_1_p_tot_sulfate_div_ks, nx_block) #else log_1_p_tot_sulfate_div_ks = LOG(arg) #endif arg = 1590.2_r8 * invtk - 12.641_r8 + 1.525_r8 * sqrtis + & log_1_m_1p005em3_s + log_1_p_tot_sulfate_div_ks #ifdef CCSMCOUPLED CALL shr_vmath_exp(arg, kf(:,iblock), nx_block) #else kf(:,iblock) = EXP(arg) #endif IF (k > 1) THEN deltaV = -9.78_r8 - (0.009_r8 + 0.000942_r8 * t) * t Kappa = (-3.91_r8 + 0.054_r8 * t) * p001 lnKfac = (-deltaV + p5 * Kappa * press_bar) * press_bar * invRtk #ifdef CCSMCOUPLED CALL shr_vmath_exp(lnKfac, Kfac, nx_block) #else Kfac = EXP(lnKfac) #endif kf(:,iblock) = kf(:,iblock) * Kfac END IF !--------------------------------------------------------------------- ! Calculate concentrations for borate, sulfate, and fluoride ! bt : Uppstrom (1974) ! st : Morris & Riley (1966) ! ft : Riley (1965) !--------------------------------------------------------------------- bt(:,iblock) = 0.000232_r8 / 10.811_r8 * scl st(:,iblock) = 0.14_r8 / 96.062_r8 * scl ft(:,iblock) = 0.000067_r8 / 18.9984_r8 * scl ! DO i = 1,nx_block ! IF (mask(i)) THEN ! END IF ! if mask ! END DO ! i loop END SUBROUTINE comp_co3_coeffs !***************************************************************************** SUBROUTINE talk_row(iblock, mask, k1, k2, x, fn, df) 4 !--------------------------------------------------------------------------- ! This routine computes TA as a function of DIC, htotal and constants. ! It also calculates the derivative of this function with respect to ! htotal. It is used in the iterative solution for htotal. In the call ! "x" is the input value for htotal, "fn" is the calculated value for ! TA and "df" is the value for dTA/dhtotal. !--------------------------------------------------------------------------- !--------------------------------------------------------------------------- ! input arguments !--------------------------------------------------------------------------- INTEGER(KIND=int_kind), INTENT(IN) :: iblock LOGICAL(KIND=log_kind), DIMENSION(nx_block), INTENT(IN) :: mask REAL(KIND=r8), DIMENSION(nx_block), INTENT(IN) :: k1, k2, x !--------------------------------------------------------------------------- ! output arguments !--------------------------------------------------------------------------- REAL(KIND=r8), DIMENSION(nx_block), INTENT(OUT) :: fn, df !--------------------------------------------------------------------------- ! local variable declarations !--------------------------------------------------------------------------- INTEGER(KIND=int_kind) :: i REAL(KIND=r8) :: & x1, x1_r, x2, x2_r, x3, k12, k12p, k123p, & a, a_r, a2_r, da, b, b_r, b2_r, db, c, c_r, & kb_p_x1_r, ksi_p_x1_r, c1_p_c_ks_x1_r_r, c1_p_kf_x1_r_r !--------------------------------------------------------------------------- DO i = 1,nx_block IF (mask(i)) THEN x1 = x(i) x1_r = c1 / x1 x2 = x1 * x1 x2_r = x1_r * x1_r x3 = x2 * x1 k12 = k1(i) * k2(i) k12p = k1p(i,iblock) * k2p(i,iblock) k123p = k12p * k3p(i,iblock) a = x3 + k1p(i,iblock) * x2 + k12p * x1 + k123p a_r = c1 / a a2_r = a_r * a_r da = c3 * x2 + c2 * k1p(i,iblock) * x1 + k12p b = x2 + k1(i) * x1 + k12 b_r = c1 / b b2_r = b_r * b_r db = c2 * x1 + k1(i) c = c1 + st(i,iblock) / ks(i,iblock) c_r = c1 / c kb_p_x1_r = c1 / (kb(i,iblock) + x1) ksi_p_x1_r = c1 / (ksi(i,iblock) + x1) c1_p_c_ks_x1_r_r = c1 / (c1 + c * ks(i,iblock) * x1_r) c1_p_kf_x1_r_r = c1 / (c1 + kf(i,iblock) * x1_r) !--------------------------------------------------------------------- ! fn = hco3+co3+borate+oh+hpo4+2*po4+silicate-hfree-hso4-hf-h3po4-ta !--------------------------------------------------------------------- fn(i) = k1(i) * dic(i,iblock) * x1 * b_r & + c2 * dic(i,iblock) * k12 * b_r & + bt(i,iblock) * kb(i,iblock) * kb_p_x1_r & + kw(i,iblock) * x1_r & + pt(i,iblock) * k12p * x1 * a_r & + c2 * pt(i,iblock) * k123p * a_r & + sit(i,iblock) * ksi(i,iblock) * ksi_p_x1_r & - x1 * c_r & - st(i,iblock) * c1_p_c_ks_x1_r_r & - ft(i,iblock) * c1_p_kf_x1_r_r & - pt(i,iblock) * x3 * a_r & - ta(i,iblock) !--------------------------------------------------------------------- ! df = d(fn)/dx !--------------------------------------------------------------------- df(i) = k1(i) * dic(i,iblock) * (b - x1 * db) * b2_r & - c2 * dic(i,iblock) * k12 * db * b2_r & - bt(i,iblock) * kb(i,iblock) * kb_p_x1_r * kb_p_x1_r & - kw(i,iblock) * x2_r & + (pt(i,iblock) * k12p * (a - x1 * da)) * a2_r & - c2 * pt(i,iblock) * k123p * da * a2_r & - sit(i,iblock) * ksi(i,iblock) * ksi_p_x1_r * ksi_p_x1_r & - c1 * c_r & - st(i,iblock) * c1_p_c_ks_x1_r_r * c1_p_c_ks_x1_r_r * (c * ks(i,iblock) * x2_r) & - ft(i,iblock) * c1_p_kf_x1_r_r * c1_p_kf_x1_r_r * kf(i,iblock) * x2_r & - pt(i,iblock) * x2 * (c3 * a - x1 * da) * a2_r END IF ! if mask END DO ! i loop END SUBROUTINE talk_row !***************************************************************************** SUBROUTINE drtsafe_row(iblock, mask_in, k1, k2, x1, x2, xacc, soln) 1,6 !--------------------------------------------------------------------------- ! Vectorized version of drtsafe, which was a modified version of ! Numerical Recipes algorithm. ! Keith Lindsay, Oct 1999 ! ! Algorithm comment : ! Iteration from Newtons method is used unless it leaves ! bracketing interval or the dx is > 0.5 the previous dx. ! In that case, bisection method is used. !--------------------------------------------------------------------------- #ifdef CCSMCOUPLED USE shr_sys_mod, ONLY : shr_sys_abort #endif !--------------------------------------------------------------------------- ! input arguments !--------------------------------------------------------------------------- INTEGER(KIND=int_kind), INTENT(IN) :: iblock LOGICAL(KIND=log_kind), DIMENSION(nx_block), INTENT(IN) :: mask_in REAL(KIND=r8), DIMENSION(nx_block), INTENT(IN) :: k1, k2, x1, x2 REAL(KIND=r8), INTENT(IN) :: xacc !--------------------------------------------------------------------------- ! output arguments !--------------------------------------------------------------------------- REAL(KIND=r8), DIMENSION(nx_block), INTENT(OUT) :: soln !--------------------------------------------------------------------------- ! local variable declarations !--------------------------------------------------------------------------- LOGICAL(KIND=log_kind) :: leave_bracket, dx_decrease LOGICAL(KIND=log_kind), DIMENSION(nx_block) :: mask INTEGER(KIND=int_kind) :: i, it REAL(KIND=r8) :: temp REAL(KIND=r8), DIMENSION(nx_block) :: xlo, xhi, flo, fhi, f, df, dxold, dx !--------------------------------------------------------------------------- ! bracket root at each location and set up first iteration !--------------------------------------------------------------------------- mask = mask_in CALL talk_row(iblock, mask, k1, k2, x1, flo, df) CALL talk_row(iblock, mask, k1, k2, x2, fhi, df) DO i = 1,nx_block IF (mask(i)) THEN IF (flo(i) .LT. c0) THEN xlo(i) = x1(i) xhi(i) = x2(i) ELSE xlo(i) = x2(i) xhi(i) = x1(i) temp = flo(i) flo(i) = fhi(i) fhi(i) = temp END IF soln(i) = p5 * (xlo(i) + xhi(i)) dxold(i) = ABS(xlo(i) - xhi(i)) dx(i) = dxold(i) END IF END DO CALL talk_row(iblock, mask, k1, k2, soln, f, df) !--------------------------------------------------------------------------- ! perform iterations, zeroing mask when a location has converged !--------------------------------------------------------------------------- DO it = 1,maxit DO i = 1,nx_block IF (mask(i)) THEN leave_bracket = ((soln(i) - xhi(i)) * df(i) - f(i)) * & ((soln(i) - xlo(i)) * df(i) - f(i)) .GE. 0 dx_decrease = ABS(c2 * f(i)) .LE. ABS(dxold(i) * df(i)) IF (leave_bracket .OR. .NOT. dx_decrease) THEN dxold(i) = dx(i) dx(i) = p5 * (xhi(i) - xlo(i)) soln(i) = xlo(i) + dx(i) IF (xlo(i) .EQ. soln(i)) mask(i) = .FALSE. ELSE dxold(i) = dx(i) dx(i) = -f(i) / df(i) temp = soln(i) soln(i) = soln(i) + dx(i) IF (temp .EQ. soln(i)) mask(i) = .FALSE. END IF IF (ABS(dx(i)) .LT. xacc) mask(i) = .FALSE. END IF END DO IF (.NOT. ANY(mask)) RETURN CALL talk_row(iblock, mask, k1, k2, soln, f, df) DO i = 1,nx_block IF (mask(i)) THEN IF (f(i) .LT. c0) THEN xlo(i) = soln(i) flo(i) = f(i) ELSE xhi(i) = soln(i) fhi(i) = f(i) END IF END IF END DO END DO ! iteration loop #ifdef CCSMCOUPLED CALL shr_sys_abort('lack of convergence in drtsafe_row') #endif END SUBROUTINE drtsafe_row !***************************************************************************** END MODULE co2calc