def compare_k_propagation( p1, p2, k_constant, test_vars, n_simulations=10000, k_uncertainty_percent=0.1 ): """Compare uncertainty propagation from the equilibrium constant internal overrides vs Monte-Carlo simulations. """ # Solve MCS par_args = dict( par1=pars_true[p1], par2=pars_true[p2], par1_type=partypes[p1], par2_type=partypes[p2], ) results = pyco2.sys(**par_args, **kwargs) # Simulate uncertainty in the selected k_constant k_value = results[k_constant] k_uncertainty = k_value * k_uncertainty_percent / 100 kwargs_u = copy.deepcopy(kwargs) kwargs_u.update( {k_constant: rng.normal(loc=k_value, scale=k_uncertainty, size=n_simulations)} ) # Solve MCS with uncertain k_constant results_u = pyco2.sys(**par_args, **kwargs_u) k_uncertainty__simulated = {k: np.std(results_u[k]) for k in test_vars} # Propagate uncertainty k_uncertainty__direct = pyco2.uncertainty.propagate_nd( results, test_vars, {k_constant: k_uncertainty}, dx=1e-6, **kwargs )[0] return k_uncertainty__simulated, k_uncertainty__direct
def compare_par1par2(i, fixedpartype, uncertainties_in): """Do uncertainties in par1, par2, and both together, propagate equivalently between the direct calculation and Monte-Carlo simulations? """ fixedpar = partypes == fixedpartype par1s_true = pars_true[~fixedpar] par1u_true = paru_true[~fixedpar] par1types = partypes[~fixedpar] par2s_true = np.full_like(par1s_true, pars_true[fixedpar][0]) par2u_true = np.full_like(par1u_true, paru_true[fixedpar][0]) par2types = np.full_like(par1types, partypes[fixedpar][0]) par1 = par1s_true[i] par1type = par1types[i] par2 = par2s_true[i] par2type = par2types[i] co2d = pyco2.sys(par1, par2, par1type, par2type, **kwargs) # Propagate directly uncertainties_in = [uncertainties_in] uncertainties, components = pyco2.uncertainty.propagate_nd( co2d, uncertainties_in, {"par1": par1u_true[i], "par2": par2u_true[i]}, **kwargs ) # Estimate the same with Monte-Carlo simulation mcsize = (10000,) par1sim = rng.normal(size=mcsize, loc=par1, scale=par1u_true[i]) par2sim = rng.normal(size=mcsize, loc=par2, scale=par2u_true[i]) co2d_par1sim = pyco2.sys(par1sim, par2, par1type, par2type, **kwargs) co2d_par2sim = pyco2.sys(par1, par2sim, par1type, par2type, **kwargs) co2d_bothsim = pyco2.sys(par1sim, par2sim, par1type, par2type, **kwargs) umc1 = np.std(co2d_par1sim[uncertainties_in[0]]) umc2 = np.std(co2d_par2sim[uncertainties_in[0]]) umcBoth = np.std(co2d_bothsim[uncertainties_in[0]]) compare1 = get_compare(umc1, components[uncertainties_in[0]]["par1"]) compare2 = get_compare(umc2, components[uncertainties_in[0]]["par2"]) compareBoth = get_compare(umcBoth, uncertainties[uncertainties_in[0]]) return compare1, compare2, compareBoth
def compare_Kunc(p1, p2, Kstr, io): """Compare uncertainty propagation from the equilibrium constant internal overrides vs Monte-Carlo simulations. """ mcsize = 10000 Kunc_pct = 0.05 co2d = pyco2.CO2SYS(np.full(mcsize, pars_true[p1]), pars_true[p2], partypes[p1], partypes[p2], *args, **kwargs) if io == "in": equilibria_in = { Kstr: np.random.normal( loc=co2d["{}{}put".format(Kstr, io)][0], scale=co2d["{}{}put".format(Kstr, io)][0] * Kunc_pct, size=mcsize, ) } co2d_mcsim = pyco2.CO2SYS(np.full(mcsize, pars_true[p1]), pars_true[p2], partypes[p1], partypes[p2], *args, **kwargs, equilibria_in=equilibria_in) elif io == "out": equilibria_out = { Kstr: np.random.normal( loc=co2d["{}{}put".format(Kstr, io)][0], scale=co2d["{}{}put".format(Kstr, io)][0] * Kunc_pct, size=mcsize, ) } co2d_mcsim = pyco2.CO2SYS(np.full(mcsize, pars_true[p1]), pars_true[p2], partypes[p1], partypes[p2], *args, **kwargs, equilibria_out=equilibria_out) testvar = "isoQ{}".format(io) testunc_Mcsim = np.std(co2d_mcsim[testvar]) uncertainties, components = pyco2.uncertainty.propagate( co2d, [testvar], { "{}{}put".format(Kstr, io): Kunc_pct * co2d["{}{}put".format(Kstr, io)][0] }, ) comparison = get_compare(testunc_Mcsim, uncertainties[testvar][0]) print(p1, p2, Kstr) if comparison > -1: print(testunc_Mcsim) print(uncertainties[testvar][0]) print(comparison) assert (comparison < 4) or (uncertainties[testvar][0] < 1e-9)
def test_pH_conversions_sys(): r1 = pyco2.sys(par1=8.1, par1_type=3, opt_pH_scale=1) r2 = pyco2.sys(par1=r1["pH_sws"], par1_type=3, opt_pH_scale=2) r3 = pyco2.sys(par1=r1["pH_free"], par1_type=3, opt_pH_scale=3) r4 = pyco2.sys(par1=r1["pH_nbs"], par1_type=3, opt_pH_scale=4) for scale in ["pH_total", "pH_sws", "pH_free", "pH_nbs"]: assert np.isclose(r1[scale], r2[scale], rtol=0, atol=1e-12) assert np.isclose(r2[scale], r3[scale], rtol=0, atol=1e-12) assert np.isclose(r3[scale], r4[scale], rtol=0, atol=1e-12)
def test_virial_coefficient(): # Calculate fugacity factors fugfac_seaflux = sf.fco2_pco2_conversion.virial_coeff( temperature_K, pres_atm) fugfac_pyco2 = pyco2.sys(2100, 8.1, 2, 3, temperature=temperature)["fugacity_factor"] # Compare results assert np.all(np.isclose(fugfac_seaflux, fugfac_pyco2, rtol=0, atol=1e-7))
def propagate_both_and_compare(k): # Propagate "both" uncertainties through PyCO2SYS and also get derivatives u_into = ["isocapnic_quotient_out"] u_from = {"{}_both".format(k): 0.5} grads_wrt = [k, "{}_out".format(k)] kwargs = dict( par1=2300, par2=8.1, par1_type=1, par2_type=3, temperature_out=0, pressure_out=10000, grads_of=u_into, grads_wrt=grads_wrt, uncertainty_into=u_into, uncertainty_from=u_from, ) results = pyco2.sys(**kwargs) u_result__both = results["u_{}".format(u_into[0])] # Assemble uncertainty matrix with complete covariance and Jacobian u_mx = np.full((2, 2), u_from["{}_both".format(k)]**2) jac = np.array([[ results["d_{}__d_{}".format(of, wrt)] for of, wrt in itertools.product(u_into, grads_wrt) ]]) u_result__jac = np.sqrt((jac @ u_mx @ jac.T)[0][0]) # Manual increment if k.startswith("p"): kk = k[1:] da = 1e-6 kwargs[kk] = 10.0**-(-np.log10(results[kk]) + da) kwargs["{}_out".format( kk)] = 10.0**-(-np.log10(results["{}_out".format(kk)]) + da) else: kk = k da__f = 1e-6 da = da__f * results[kk] kwargs[kk] = results[kk] + da kwargs["{}_out".format(kk)] = results["{}_out".format(kk)] + da results_manual = pyco2.sys(**kwargs) u_result__manual = np.abs(results_manual[u_into[0]] - results[u_into[0]]) u_result__manual *= u_from["{}_both".format(k)] u_result__manual /= da return u_result__both, u_result__jac, u_result__manual
def test_fCO2_to_pCO2_conversion(): # Convert fCO2 to pCO2 pCO2_pyco2 = pyco2.sys(pCO2_or_fCO2, 8.1, 5, 3, temperature=temperature)["pCO2"] pCO2_seaflux = sf.fCO2_to_pCO2(pCO2_or_fCO2, temperature) # Compare results - not perfect agreement, but "good enough"(?) i.e. <0.01 microatm not_nan = ~np.isnan(pCO2_seaflux) assert np.all( np.isclose(pCO2_pyco2[not_nan], pCO2_seaflux[not_nan], rtol=0, atol=0.01))
def compare_par1par2(i, fixedpartype, uncertainties_in): fixedpar = partypes == fixedpartype par1s_true = pars_true[~fixedpar] par1u_true = paru_true[~fixedpar] par1types = partypes[~fixedpar] par2s_true = np.full_like(par1s_true, pars_true[fixedpar][0]) par2u_true = np.full_like(par1u_true, paru_true[fixedpar][0]) par2types = np.full_like(par1types, partypes[fixedpar][0]) par1 = par1s_true[i] par1type = par1types[i] par2 = par2s_true[i] par2type = par2types[i] co2d = pyco2.CO2SYS(par1, par2, par1type, par2type, *args, **kwargs) # Propagate directly uncertainties_in = [uncertainties_in] uncertainties, components = pyco2.uncertainty.propagate( co2d, uncertainties_in, { "PAR1": par1u_true[i], "PAR2": par2u_true[i] }) # Estimate the same with Monte-Carlo simulation mcsize = (10000, ) par1sim = np.random.normal(size=mcsize, loc=par1, scale=par1u_true[i]) par2sim = np.random.normal(size=mcsize, loc=par2, scale=par2u_true[i]) co2d_par1sim = pyco2.CO2SYS(par1sim, par2, par1type, par2type, *args, **kwargs) co2d_par2sim = pyco2.CO2SYS(par1, par2sim, par1type, par2type, *args, **kwargs) co2d_bothsim = pyco2.CO2SYS(par1sim, par2sim, par1type, par2type, *args, **kwargs) umc1 = np.std(co2d_par1sim[uncertainties_in[0]]) umc2 = np.std(co2d_par2sim[uncertainties_in[0]]) umcBoth = np.std(co2d_bothsim[uncertainties_in[0]]) compare1 = get_compare(umc1, components[uncertainties_in[0]]["PAR1"]) compare2 = get_compare(umc2, components[uncertainties_in[0]]["PAR2"]) compareBoth = get_compare(umcBoth, uncertainties[uncertainties_in[0]]) return compare1, compare2, compareBoth
def run_co2sys_ta_ph(ta, ph, sal, temp=25, press_dbar=0): """ Runs the PyCO2SYS function using input TA and pH data. opt_pH_scale=1 is the default (Total scale), including here for clarity. opt_k_carbonic=4 is MCHP73 (Mehrbach et al 1973) refit by DM87 (Dickson & Millero 1987) opt_k_bisulfate=1 is the default (Dickson 1990), including here for clarity opt_total_borate=1 is the default (Uppstrom 1974), including here for clarity opt_k_fluoride=2 is PF87 (Perez & Fraga 1987) :param ta: Total Alkalinity array :param ph: pH array :param sal: salinity array :param temp: temperature array, default=25 :param press_dbar: pressure (in units=dbar) array, default=0 :return: calculated aragonite saturation, pCO2, revelle factor """ # define input conditions par1 = ta # Total Alkalinity par1_type = 1 # parameter 1 type (TA) par2 = ph par2_type = 3 # parameter 2 type (pH) kwargs = dict(salinity=sal, temperature=temp, pressure=press_dbar, opt_pH_scale=1, opt_k_carbonic=4, opt_k_bisulfate=1, opt_total_borate=1, opt_k_fluoride=2) results = pyco2.sys(par1, par2, par1_type, par2_type, **kwargs) omega_arag = results['saturation_aragonite'] # aragonite saturation state pco2 = results['pCO2'] # units = uatm revelle = results['revelle_factor'] return omega_arag, pco2, revelle
pyco2.sys( co2inputs[0], co2inputs[1], co2inputs[2], co2inputs[3], salinity=co2inputs[4], temperature=co2inputs[5], temperature_out=co2inputs[6], pressure=co2inputs[7], pressure_out=co2inputs[8], total_silicate=co2inputs[9], total_phosphate=co2inputs[10], total_ammonia=co2inputs[11], total_sulfide=co2inputs[12], opt_pH_scale=co2inputs[13], opt_k_carbonic=co2inputs[14], opt_k_bisulfate=co2inputs[15], opt_k_fluoride=co2inputs[16], opt_total_borate=co2inputs[17], opt_gas_constant=3, grads_of=[ "alkalinity", "dic", "pCO2", "fCO2", "bicarbonate", "carbonate", "aqueous_CO2", "xCO2", "pCO2_out", "fCO2_out", "bicarbonate_out", "carbonate_out", "aqueous_CO2_out", "xCO2_out", ], grads_wrt=["temperature", "salinity", "k_CO2", "k_carbonic_1", "k_carbonic_2"], )
import PyCO2SYS as pyco2, numpy as np # Run PyCO2SYS to begin with some silicate args = (2250, [2100, 8.1, 400, 150, 500, 10000], 1, [2, 3, 4, 5, 2, 2]) opt_pH_scale = 3 r0 = pyco2.sys(*args, total_silicate=100, opt_pH_scale=3) # Add alpha instead of silicate r1 = pyco2.sys( *args, total_silicate=0, total_alpha=r0["total_silicate"], k_alpha=r0["k_silicate"], opt_pH_scale=opt_pH_scale, ) # Add beta instead of fluoride - note HF used in pK scale conversions so you have to # provide them all ready-converted r2 = pyco2.sys( *args, total_silicate=r0["total_silicate"], total_fluoride=0, total_beta=r0["total_fluoride"], k_beta=r0["k_fluoride"], k_carbonic_1=r0["k_carbonic_1"], k_carbonic_2=r0["k_carbonic_2"], k_water=r0["k_water"], k_borate=r0["k_borate"], k_bisulfate=r0["k_bisulfate"], k_silicate=r0["k_silicate"], opt_pH_scale=opt_pH_scale,
import copy import PyCO2SYS as pyco2, numpy as np # Seed random number generator for reproducibility rng = np.random.default_rng(7) # Generate inputs npts = 1000 alkalinity = rng.normal(loc=2300, scale=500, size=npts) dic = rng.normal(loc=2100, scale=500, size=npts) kwargs = dict(par1=alkalinity, par2=dic, par1_type=1, par2_type=2,) # Solve the marine carbonate system r_explicit = pyco2.sys(**kwargs, buffers_mode="explicit") r_automatic = pyco2.sys(**kwargs, buffers_mode="auto") # Compare the buffers buffers = [ "beta_alk", "beta_dic", "gamma_alk", "gamma_dic", "omega_alk", "omega_dic", "psi", "isocapnic_quotient", "revelle_factor", ] buffers_difference = {} buffers_percent = {} for buffer in buffers:
phos = 3 h2s = 0.12 nh3 = 0.5 k1k2c = 16 kso4c = 3 phscale = 3 # - get the co2dict co2dict = pyco2.CO2SYS( par1, par2, par1type, par2type, sal, tempin, tempout, presin, presout, si, phos, phscale, k1k2c, kso4c, H2S=h2s, NH3=nh3, ) # - propagate the uncertainties grads_of = "all" grads_wrt = "all" co2derivs, dxs = pyco2.uncertainty.forward( co2dict, grads_of, grads_wrt,
"opt_k_fluoride": rng.integers(size=npts, low=1, high=2, endpoint=True), "opt_gas_constant": 3, "buffers_mode": "none", "total_alpha": rng.uniform(size=npts, low=0, high=100), "k_alpha": rng.uniform(size=npts, low=2, high=12), "total_beta": rng.uniform(size=npts, low=0, high=100), "k_beta": rng.uniform(size=npts, low=2, high=12), } # Remove Peng options (bad alkalinity equation!) opt_kc = pyco2_kwargs["opt_k_carbonic"] pyco2_kwargs["opt_k_carbonic"][opt_kc == 7] = 16 # Set MCS parameters and solve with PyCO2SYS pH = np.append(rng.uniform(size=npts - 1, low=3, high=10), 8.1) dic = np.append(rng.uniform(size=npts - 1, low=0, high=5000), 0) results_pyco2 = pyco2.sys(pH, dic, 3, 2, **pyco2_kwargs) alkalinity_pyco2 = results_pyco2["alkalinity"] # Solve with Calkulate results_for_calk = { k: v * 1e-6 if k.startswith("total_") or k == "dic" else v for k, v in results_pyco2.items() } components_calk = calk.simulate.alkalinity_components(pH, results_for_calk, results_for_calk) alkalinity_calk = calk.simulate.alkalinity(pH, results_for_calk, results_for_calk) * 1e6 # Compare individual components results_pyco2["H"] = results_pyco2["Hfree"] results_pyco2["alk_alpha"] = results_pyco2["alkalinity_alpha"]
import numpy as np, PyCO2SYS as pyco2 # Calculate initial results kwargs = { "salinity": 32, "k_carbonic_1": 1e-6, } par1 = np.linspace(2000, 2100, 11) par2 = np.vstack(np.linspace(2300, 2400, 21)) par1_type = 2 par2_type = 1 results = pyco2.sys(par1, par2, par1_type, par2_type, **kwargs) # Get gradients grads_of = ["pH", "k_carbonic_1"] grads_wrt = ["par1", "par2", "k_carbonic_1", "pk_carbonic_1", "temperature"] CO2SYS_derivs, dxs = pyco2.uncertainty.forward_nd( results, grads_of, grads_wrt, **kwargs ) # Do independent uncertainty propagation uncertainties_into = ["pH", "isocapnic_quotient", "dic"] uncertainties_from = { "par1": 2, "par2": 2, "pk_carbonic_1": 0.02, } uncertainties, components = pyco2.uncertainty.propagate_nd( results, uncertainties_into, uncertainties_from, **kwargs, )
# Import MATLAB results and recalculate with PyCO2SYS matlab = pd.read_csv( "manuscript/results/compare_equilibrium_constants_v3_1_1.csv") python = pd.DataFrame( pyco2.sys( matlab.PAR1.values, matlab.PAR2.values, matlab.PAR1TYPE.values, matlab.PAR2TYPE.values, salinity=matlab.SAL.values, temperature=matlab.TEMPIN.values, temperature_out=matlab.TEMPOUT.values, pressure=matlab.PRESIN.values, pressure_out=matlab.PRESOUT.values, opt_pH_scale=matlab.pHSCALEIN.values, opt_gas_constant=3, opt_k_carbonic=matlab.K1K2CONSTANTS.values, opt_total_borate=matlab.BORON.values, opt_k_bisulfate=matlab.KSO4CONSTANT.values, opt_k_fluoride=matlab.KFCONSTANT.values, total_phosphate=matlab.PO4.values, total_silicate=matlab.SI.values, total_ammonia=matlab.TNH4.values, total_sulfide=matlab.TH2S.values, buffers_mode="none", )) def test_equilibrium_constants(): for m, p in ( ("K0input", "k_CO2"),
"pCO2_out", "fCO2_out", "bicarbonate_out", "carbonate_out", "aqueous_CO2_out", "saturation_calcite_out", "saturation_aragonite_out", "xCO2_out", ] uncert_from = {"{}_both".format(k): v for k, v in pyco2.uncertainty.pKs_OEDG18.items()} # uncert_from = {"{}_out".format(k): v for k, v in pyco2.uncertainty.pKs_OEDG18.items()} # uncert_from.update(pyco2.uncertainty.pKs_OEDG18) uncert_from["total_borate__f"] = pyco2.uncertainty_OEDG18["total_borate__f"] uncert_from.update({"par1": co2ml.UPAR1.values, "par2": co2ml.UPAR2.values}) results = pd.DataFrame( pyco2.sys(**kwargs, uncertainty_into=uncert_into, uncertainty_from=uncert_from) ) pyco2sys_u = pd.DataFrame( { "u_TAlk_": results.u_alkalinity, "u_TCO2_": results.u_dic, "u_pCO2in_": results.u_pCO2, "u_fCO2in_": results.u_fCO2, "u_HCO3in_": results.u_bicarbonate, "u_CO3in_": results.u_carbonate, "u_CO2in_": results.u_aqueous_CO2, "u_OmegaCAin_": results.u_saturation_calcite, "u_OmegaARin_": results.u_saturation_aragonite, "u_xCO2in_": results.u_xCO2, "u_pCO2out_": results.u_pCO2_out, "u_fCO2out_": results.u_fCO2_out,
# Assemble inputs into dicts if np.isscalar(temperature): temperature = np.array([temperature]) if np.isscalar(salinity): salinity = np.array([salinity]) kwargs_core = dict( temperature=temperature, salinity=salinity, opt_pH_scale=3, opt_k_carbonic=16, opt_total_borate=1, ) # Calculate total alkalinity co2sys_core = pyco2.sys(dic, pH_free, 2, 3, **kwargs_core) alkalinity_core = co2sys_core["alkalinity"] # Simulate titration(s) dilution_factor = sample_mass / (sample_mass + titrant_mass) alkalinity_titration = (1e6 * (sample_mass * co2sys_core["alkalinity"] * 1e-6 - titrant_mass * titrant_molinity) / (sample_mass + titrant_mass)) dic_titration = dic * dilution_factor kwargs_titration = copy.deepcopy(kwargs_core) kwargs_titration.update( total_borate=co2sys_core["total_borate"], total_fluoride=co2sys_core["total_fluoride"], total_sulfate=co2sys_core["total_sulfate"], ) for k in [
import numpy as np import PyCO2SYS as pyco2 # Set conditions alkalinity = 0 dic = 2500 # Get all pHs from TA and DIC opt_pH_scale = np.array([1, 2, 3, 4]) results_original = pyco2.sys(alkalinity, dic, 1, 2, opt_pH_scale=opt_pH_scale) # Get all pHs from TA and DIC, again results_fixed = pyco2.sys(alkalinity, dic, 1, 2, opt_pH_scale=np.array([1, 2, 3, 4])) # Check they're consistent regardless of input scale def test_pH_scale_consistency(): assert np.all( np.isclose(results_fixed["pH_total"], results_fixed["pH"][opt_pH_scale == 1])) assert np.all( np.isclose(results_fixed["pH_sws"], results_fixed["pH"][opt_pH_scale == 2])) assert np.all( np.isclose(results_fixed["pH_free"], results_fixed["pH"][opt_pH_scale == 3])) assert np.all(
import pandas as pd, numpy as np import PyCO2SYS as pyco2 # Use the EZIO function filestem = "tests/data/EZIO_input_template" df = pyco2.ezio(filestem + ".xlsx") df_cols = [c for c in df.columns if c != "-"] df_numpy = df[df_cols].to_numpy(dtype=float) df_saved = pd.read_csv(filestem + "_processed.csv") def test_ezio_df(): """Does the EZIO function return a pandas DataFrame with non-NaN values?""" assert isinstance(df, pd.DataFrame) assert ~np.any(np.isnan(df_numpy)) def test_ezio_output(): """Does the EZIO function save its results to a file?""" assert isinstance(df_saved, pd.DataFrame) for col in df_cols: assert np.all(np.isclose(df[col].to_numpy(dtype=float), df_saved[col])) # test_ezio_df() # test_ezio_output()
"SAL", "TEMPIN", "TEMPOUT", "PRESIN", "PRESOUT", "SI", "PO4", "pHSCALEIN", "K1K2CONSTANTS", "KSO4CONSTANTS", "NH3", "H2S", "KFCONSTANT", ] ] co2py = pyco2.CO2SYS(*co2inputs, buffers_mode="auto") # Get override input dicts from co2py and use them in CO2SYS totals = pyco2.engine.dict2totals_umol(co2py) equilibria_in, equilibria_out = pyco2.engine.dict2equilibria(co2py) co2py_override = pyco2.CO2SYS(*co2inputs, buffers_mode="auto", totals=totals, equilibria_in=equilibria_in, equilibria_out=equilibria_out) # Compare results - should be ~identical co2py_diff = { k: co2py_override[k] - co2py[k] for k in co2py if k != "buffers_mode" }
test_conditioning() #%% Now move on to the main function par1 = [2300, 2150, 8.3] par2 = [2150] par1_type = [1, 1, 3] par2_type = 2 kwargs = { "salinity": [[35, 31, 0.8], [34, 34, 34]], "total_sulfate": [[3], [5]], "k_carbonic_1": [1e-6, 1.1e-6, 1.2e-6], "temperature_out": 0, } co2nd = pyco2.sys(par1, par2, par1_type, par2_type, **kwargs) def test_nd_misc(): assert (np.shape(co2nd["k_carbonic_2"]) == pyco2.engine.nd.broadcast1024( par1, par2, par1_type, par2_type, *kwargs.values()).shape) test_nd_misc() # Test with all scalar inputs co2nd_scalar = pyco2.CO2SYS_nd(2300, 2150, 1, 2) def test_scalars(): assert np.all([np.size(v) == 1 for v in co2nd_scalar.values()])
# Import packages import PyCO2SYS as pyco2 import pandas as pd # Why not pyco2.say_hello() # Define test conditions par1 = 2300 # parameter 1, here total alkalinity in μmol/kg-sw par2 = 8.1 # parameter 2, here pH on the Total scale par1type = 1 # "parameter 1 is total alkalinity" par2type = 3 # "parameter 2 is pH" sal = 33 # practical salinity temp = 22 # temperature in °C pres = 1000 # pressure in dbar si = 10 # total silicate in μmol/kg-sw phos = 1 # total phosphate in μmol/kg-sw nh3 = 2 # total ammonia in μmol/kg-sw h2s = 3 # total sulfide in μmol/kg-sw pHscale = 1 # "input pH is on the Total scale" k1k2c = 10 # "use LDK00 constants for carbonic acid dissociation" kso4c = 3 # "use D90a for bisulfate dissociation and LKB10 for borate:salinity" # Run the test res, diff = pyco2.test.roundrobin( par1, par2, par1type, par2type, sal, temp,
test_co2py_matlab() # Compare new n-d approach co2nd = pd.DataFrame( pyco2.sys( co2inputs[0], co2inputs[1], co2inputs[2], co2inputs[3], salinity=co2inputs[4], temperature=co2inputs[5], temperature_out=co2inputs[6], pressure=co2inputs[7], pressure_out=co2inputs[8], total_silicate=co2inputs[9], total_phosphate=co2inputs[10], total_ammonia=co2inputs[11], total_sulfide=co2inputs[12], opt_pH_scale=co2inputs[13], opt_k_carbonic=co2inputs[14], opt_k_bisulfate=co2inputs[15], opt_k_fluoride=co2inputs[16], opt_total_borate=co2inputs[17], opt_gas_constant=3, ) ) def test_nd(): assert np.all(co2nd.isocapnic_quotient_out.values == co2py.isoQout.values)
orr2 = pd.read_csv("manuscript/data/orr2018-table2.csv") orr = dict( par1=2300, par2=2000, par1_type=1, par2_type=2, temperature=18, salinity=35, total_phosphate=0, total_silicate=0, opt_k_carbonic=10, opt_total_borate=2, ) grads_of = [c for c in orr2.columns if c not in ["wrt", "program"]] grads_wrt = ["par1", "par2", "temperature", "salinity"] results = pyco2.sys(**orr, grads_of=grads_of, grads_wrt=grads_wrt) for wrt in grads_wrt: if wrt == "par1": wrt_label = "alkalinity" elif wrt == "par2": wrt_label = "dic" else: wrt_label = wrt nrow = pd.Series({"wrt": wrt_label, "program": "PyCO2SYS"}) for of in grads_of: nrow[of] = results["d_{}__d_{}".format(of, wrt)] if of == "Hfree": nrow[of] *= 1e3 orr2 = orr2.append(nrow, ignore_index=True) orr2_groups = orr2.groupby("wrt").mean() orr2.set_index(["wrt", "program"], inplace=True)
titrant_molinity = 0.3 # mol/kg titrant_mass = np.arange(0, 2.51, 0.05) * 1e-3 # kg dilution_factor = sample_mass / (sample_mass + titrant_mass) # Dilute alkalinity etc. through the titration alkalinity = ( 1e6 * (sample_mass * alkalinity * 1e-6 - titrant_molinity * titrant_mass) / (sample_mass + titrant_mass) ) dic *= dilution_factor for k in ["total_borate", "total_fluoride", "total_sulfate"]: kwargs[k] *= dilution_factor # Solve for pH, no phosphate results_pH = pyco2.CO2SYS_nd(alkalinity, dic, 1, 2, **kwargs) pH = results_pH["pH_free"] # And again, with phosphate kwargs.update( { "total_phosphate": 10 * dilution_factor, "k_phosphate_1": 1 / 5.68e1, "k_phosphate_2": 8e-7, "k_phosphate_3": 1.32e-15 / 8e-7, } ) pH_phosphate = pyco2.CO2SYS_nd(alkalinity, dic, 1, 2, **kwargs)["pH_free"] # Compare with D81's tables d81_pH = np.genfromtxt(
salinity=33, # practical salinity temperature=22, # temperature in °C pressure=1234, # pressure in dbar total_silicate=10, # total silicate in μmol/kg-sw total_phosphate=1, # total phosphate in μmol/kg-sw total_ammonia=2, # total ammonia in μmol/kg-sw total_sulfide=3, # total sulfide in μmol/kg-sw total_alpha=5, k_alpha=1e-4, total_beta=6, k_beta=1e-8, buffers_mode="none", # don't bother calculating buffer factors ) # Solve the system initially results = pyco2.sys(par1, par2, par1_type, par2_type, **kwargs) # Define parameter types and names in output partypes = { 1: "alkalinity", 2: "dic", 3: "pH", 4: "pCO2", 5: "fCO2", 6: "carbonate", 7: "bicarbonate", 8: "aqueous_CO2", 9: "xCO2", }
"PAR1TYPE", "PAR2TYPE", "SAL", "TEMPIN", "TEMPOUT", "PRESIN", "PRESOUT", "SI", "PO4", "pHSCALEIN", "K1K2CONSTANTS", "KSO4CONSTANTS", ] ] go = time() co2py = pyco2.CO2SYS(*co2inputs, buffers_mode="none") print("PyCO2SYS runtime = {:.6f} s".format(time() - go)) co2py = pd.DataFrame(co2py) # short = ((co2inputs[2] == 1) & (co2inputs[3] == 4)) | ( # (co2inputs[2] == 4) & (co2inputs[3] == 1) # ) # short_ix = co2py.index[short] # co2py_short = pd.DataFrame( # pyco2.CO2SYS(*[i[short] for i in co2inputs], buffers_mode="none") # ) # short_diff = co2py.loc[short_ix, "BAlkin"].values - co2py_short["BAlkin"].values # Also test the original CO2SYS clone go = time() DATA, HEADERS, _ = pyco2.original.CO2SYS(*co2inputs)
ta = 2250 tmp = 25 sal = 35 prs = 0 si = 0 phos = 0 # Run CO2SYS cdict = pyco2.sys( ta, dic, 1, 2, salinity=sal, temperature=tmp, pressure=prs, total_silicate=si, total_phosphate=phos, opt_pH_scale=1, opt_k_carbonic=10, # not sure which options were used by ESM10... opt_k_fluoride=1, opt_total_borate=2, buffers_mode="auto", ) # Recreate ESM10 Fig. 2 fvars = [ "gamma_dic", "gamma_alk", "beta_dic", "beta_alk", "omega_dic", "omega_alk" ] fmults = [1, -1, 1, -1, -1, 1] fclrs = ["#453c90", "#b84690", "#b84690", "#007831", "#df0023", "#40b4b6"] flabels = [
import numpy as np import PyCO2SYS as pyco2 # Set conditions alkalinity = 0 dic = 2500 # Get all pHs from TA and DIC opt_pH_scale = np.array([1, 2, 3, 4]) results = pyco2.sys(alkalinity, dic, 1, 2, opt_pH_scale=np.array([1, 2, 3, 4])) # Check they're consistent regardless of input scale def test_pH_scale_consistency(): assert np.all( np.isclose(results["pH_total"], results["pH"][opt_pH_scale == 1])) assert np.all( np.isclose(results["pH_sws"], results["pH"][opt_pH_scale == 2])) assert np.all( np.isclose(results["pH_free"], results["pH"][opt_pH_scale == 3])) assert np.all( np.isclose(results["pH_nbs"], results["pH"][opt_pH_scale == 4])) # test_pH_scale_consistency()