def pcomp(par, opar): diffs = [] Ks = MyAMI_K_calc(param_dict=par) oKs = MyAMI_K_calc(param_dict=opar) for k in Ks.keys(): diffs.append(Ks[k] - oKs[k]) return all(abs(np.array(diffs)) < 1e-6)
def test_CompareToDickson2007(self): # Check params @ 25ºC and 35 PSU # Parameters are from Dickson, Sabine & Christian # (Guide to best practises for ocean CO2 measurements, # PICES Special Publication, 2007), Chapter 5.7.2 (seawater). # Except KspC and KspA, which are from from Zeebe & # Wolf-Gladrow, 2001, Appendix A.10 K_ckeck = { 'K0': np.exp(-3.5617), 'K1': 10**(-5.8472), 'K2': 10**(-8.9660), 'KB': np.exp(-19.7964), # 'KW': np.exp(-30.434), 'KSO4': np.exp(-2.30), 'KspC': 10**-6.3693, 'KspA': 10**-6.1883 } Ks = MyAMI_K_calc() for k, p in K_ckeck.items(): self.assertAlmostEqual(Ks[k] / p, 1, places=3, msg='failed on ' + k) return
def calc_Ks_TS(T, S, P, Ks={}): """ Helper function to calculate Ks given only T(C), S and P. If Ks is a dict, the Ks provided in the dict are used transparrently (i.e. no pressure modification). """ Mg = 0.0528171 Ca = 0.0102821 if isinstance(Ks, dict): given_Ks = Ks Ks = MyAMI_K_calc(TempC=T, Sal=S, P=P, Mg=Mg, Ca=Ca) # non-MyAMI Constants Ks.update(calc_KPs(T, S, P)) Ks.update(calc_KF(T, S, P)) Ks.update(calc_KSi(T, S, P)) # pH conversions to total scale. # - KP1, KP2, KP3 are all on SWS # - KSi is on SWS # - MyAMI KW is on SWS... DOES THIS MATTER? TS = calc_TS(S) TF = calc_TF(S) SWStoTOT = (1 + TS / Ks.KSO4) / (1 + TS / Ks.KSO4 + TF / Ks.KF) # FREEtoTOT = 1 + 'T_' + mode]S / Ks.KSO4 conv = ["KP1", "KP2", "KP3", "KSi", "KW"] for c in conv: Ks[c] *= SWStoTOT Ks.update(given_Ks) return Ks
def get_Ks(ps): """ Helper function to calculate Ks. If ps.Ks is a dict, those Ks are used transparrently, with no pressure modification. """ if isinstance(ps.Ks, dict): Ks = Bunch(ps.Ks) else: if maxL(ps.Mg, ps.Ca) == 1: if ps.Mg is None: ps.Mg = 0.0528171 if ps.Ca is None: ps.Ca = 0.0102821 Ks = MyAMI_K_calc(TempC=ps.T, Sal=ps.S, Mg=ps.Mg, Ca=ps.Ca, P=ps.P) else: # if only Ca or Mg provided, fill in other with modern if ps.Mg is None: ps.Mg = 0.0528171 if ps.Ca is None: ps.Ca = 0.0102821 # calculate Ca and Mg specific Ks Ks = MyAMI_K_calc_multi(TempC=ps.T, Sal=ps.S, Ca=ps.Ca, Mg=ps.Mg, P=ps.P) # non-MyAMI Constants Ks.update(calc_KPs(ps.T, ps.S, ps.P)) Ks.update(calc_KF(ps.T, ps.S, ps.P)) Ks.update(calc_KSi(ps.T, ps.S, ps.P)) # pH conversions to total scale. # - KPn are all on SWS # - KSi is on SWS # - MyAMI KW is on SWS... DOES THIS MATTER? SWStoTOT = (1 + ps.TS / Ks.KSO4) / (1 + ps.TS / Ks.KSO4 + ps.TF / Ks.KF) # FREEtoTOT = 1 + ps.TS / Ks.KSO4 conv = ['KP1', 'KP2', 'KP3', 'KSi', 'KW'] for c in conv: Ks[c] *= SWStoTOT return Ks
def calc_Ks(T, S, P, Mg, Ca, TS, TF, Ks=None): """ Helper function to calculate Ks. If Ks is a dict, those Ks are used transparrently (i.e. no pressure modification). """ if isinstance(Ks, dict): Ks = Bunch(Ks) else: if maxL(Mg, Ca) == 1: if Mg is None: Mg = 0.0528171 if Ca is None: Ca = 0.0102821 Ks = MyAMI_K_calc(TempC=T, Sal=S, P=P, Mg=Mg, Ca=Ca) else: # if only Ca or Mg provided, fill in other with modern if Mg is None: Mg = 0.0528171 if Ca is None: Ca = 0.0102821 # calculate Ca and Mg specific Ks Ks = MyAMI_K_calc_multi(TempC=T, Sal=S, P=P, Ca=Ca, Mg=Mg) # non-MyAMI Constants Ks.update(calc_KPs(T, S, P)) Ks.update(calc_KF(T, S, P)) Ks.update(calc_KSi(T, S, P)) # pH conversions to total scale. # - KP1, KP2, KP3 are all on SWS # - KSi is on SWS # - MyAMI KW is on SWS... DOES THIS MATTER? SWStoTOT = (1 + TS / Ks.KSO4) / (1 + TS / Ks.KSO4 + TF / Ks.KF) # FREEtoTOT = 1 + 'T_' + mode]S / Ks.KSO4 conv = ["KP1", "KP2", "KP3", "KSi", "KW"] for c in conv: Ks[c] *= SWStoTOT return Ks
def test_CompareToMehrbachData(self): """ Compares pK1 and pK2 calcualted by MyAMI_V2 to data from Mehrbach et al (1973), as per Lueker et al (2000). Test data on Total pH scale taken from Table 2 of Lueker et al (2000) """ # read data lk = pd.read_csv( "cbsyst/test_data/Lueker2000/Lueker2000_Table2.csv", comment="#" ) # calculate MyAMI Ks mKs = MyAMI_K_calc(lk.TempC, lk.Sal) # calculate pK1 and pK2 2 residuals rpK1 = lk.pK1 - -np.log10(mKs.K1) rpK2 = lk.pK2 - -np.log10(mKs.K2) # calculate median and 95% CI of residuals rpK1_median = rpK1.median() rpK1_95ci = np.percentile(rpK1[~np.isnan(rpK1)], (2.5, 97.5)) self.assertLessEqual( abs(rpK1_median), 0.005, msg="Median offset from Mehrbach (1973) pK1." ) self.assertTrue( all(abs(rpK1_95ci) <= 0.02), msg="95% CI of difference from Mehrbach pK1 <= 0.02", ) rpK2_median = rpK1.median() rpK2_95ci = np.percentile(rpK2[~np.isnan(rpK2)], (2.5, 97.5)) self.assertLessEqual( abs(rpK2_median), 0.005, msg="Median offset from Mehrbach (1973) pK2." ) self.assertTrue( all(abs(rpK2_95ci) <= 0.02), msg="95% CI of difference from Mehrbach pK2 <= 0.02", ) return