├── .gitignore ├── LICENSE ├── README.md ├── comparisons ├── CO2SYSigen.m ├── calculate_derivatives.m ├── compare_to_Orr_et_al_2018.m ├── compare_versions.m ├── compare_versions_uncert.m ├── data │ ├── orr2018-table2.csv │ ├── orr2018-table3.csv │ └── orr2018-table4.csv └── derivatives │ ├── bor_v3.csv │ ├── k0_v3.csv │ ├── k1_v3.csv │ ├── k2_v3.csv │ ├── kb_v3.csv │ ├── kw_v3.csv │ ├── par1_v3.csv │ ├── par2_v3.csv │ ├── phos_v3.csv │ ├── sal_v3.csv │ ├── sil_v3.csv │ └── temp_v3.csv ├── main ├── CO2SYS.m ├── TOTALS.m ├── derivnum.m ├── errors.m └── example_CO2SYS.m └── v2_0_5_compatible ├── CO2SYS_adjusted_to_v2_0_5.m ├── derivnum_adjusted_to_v2_0_5.m └── errors_adjusted_to_v2_0_5.m /.gitignore: -------------------------------------------------------------------------------- 1 | # MATLAB autosaves 2 | *.m~ 3 | *.asv 4 | 5 | # macOS folder file 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Jonathan D. Sharp 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CO2-System-Extd 2 | 3 | DOI [![View CO2SYSv3 for MATLAB on File Exchange](https://www.mathworks.com/matlabcentral/images/matlab-file-exchange.svg)](https://www.mathworks.com/matlabcentral/fileexchange/78378-co2sysv3-for-matlab) 4 | 5 | ## ABOUT 6 | 7 | This repository includes software compatible with MATLAB and GNU Octave for calculating marine CO2 system variables (CO2SYS.m), computing partial derivatives of calculated CO2 system variables with respect to inputs (derivnum.m), and propagating uncertainties for CO2 system calculations (errors.m). This software performs similarly to previously released versions of CO2SYS.m (v1: https://cdiac.ess-dive.lbl.gov/ftp/co2sys/CO2SYS_calc_MATLAB_v1.1/; v2: https://github.com/jamesorr/CO2SYS-MATLAB), and includes the following extended capabilities, additions, and bug fixes (among other minor changes): 8 | 9 | 1) Can accept input parameters of [CO3], [HCO3], and [CO2], and propagate their uncertainties 10 | 2) Includes NH3 and HS as alkalinity contributors, and propagates their uncertainties 11 | 3) Uses separate inputs to specify choices for characterizations of K1K2, KSO4, KF, and TB 12 | 4) Does not evaluate input parameters equal to -999 or NaN 13 | 5) Exits pH iteration loops that do not converge and indicates where a problem occurred 14 | 6) Provides exactly identical pH results for a given input line, no matter the other lines of input parameters (this is not necessarily the case for prior versions of CO2SYS.m) 15 | 7) Uses an updated definition of the ideal gas constant (https://physics.nist.gov/cgi-bin/cuu/Value?r) 16 | 8) Fixes bugs in CO2SYS.m Revelle factor calculation and derivnum.m output conditions 17 | 9) Includes K1 and K2 constants defined by Sulpis et al. (2020), K2 constant defined by Schockman and Byrne (2021), KF constant defined by Perez and Fraga (1987), and KSO4 constant of Waters and Millero (2013) 18 | 10) Determines initial pH in iterative solvers using the approach of Munhoven (2013), detailed further in Humphreys et al. (submitted), rather than simply using an initial estimate of 8.0 each time. 19 | 11) Obtains free scale pH properly within iterative pH solvers no matter the input scale, rather than making the simplification that input pH is always on the total scale. 20 | 12) Includes substrate-inhibitor ratio (Bach, 2015) as an output argument from CO2SYS. 21 | 13) Calculates uncertainties at output conditions that are associated with equilibrium constants with respect to equilibrium constants at output conditions, rather than input conditions as previously. This essentially assume pK uncertainty is constant regardless of temperature and pressure. 22 | 14) Calculates derivatives and errors for the Revelle factor. 23 | 15) errors.m includes optional calcium concentration uncertainty input as discussed in Dillon et al. (2020) 24 | 16) Option added for pressure corrections to K0 and fugacity factor. 25 | 26 | Also included in this repository is a routine to compare CO2SYSv3 to CO2SYSv2 (compare_versions.m), a routine to calculate total concentrations of conservative elements (Na, Mg, Cl, etc.) from CO2SYS output (TOTALS.m), and an example function to run CO2SYSv3 and plot some of the output (example_CO2SYS.m). 27 | 28 | ## HISTORY 29 | 30 | CO2SYS was initially developed by Lewis and Wallace (1998) for MS DOS, later adapted for MS Excel and MATLAB by Pierrot (2006). The code was vectorized, refined, and optimized for computational speed by van Heuven (2011). Options for error propagation were added by Orr et al. (2018). This software builds upon those previous versions. 31 | 32 | ## INSTALLATION AND USE 33 | 34 | Download the files in this repository and place them in a directory that is in the MATLAB search path. Or add the directory where they are located to the search path (https://www.mathworks.com/help/matlab/matlab_env/add-remove-or-reorder-folders-on-the-search-path.html). 35 | 36 | To perform CO2 system calculations, use CO2SYS.m as directed in the function's help text. All sub-routines that will be called upon are contained within the CO2SYS.m function. 37 | 38 | To propagate uncertainties in CO2 system calculations, use errors.m as directed in the function's help text. The errors.m routine will call upon CO2SYS.m and derivnum.m. As such, this version of errors.m is compatible only with CO2SYSv3. 39 | 40 | To compute partial derivatives of calculated CO2 system variables with respect to input parameters, use derivnum.m as directed in the function's help text. The derivnum.m routine will call upon CO2SYS.m. As such, this version of derivnum.m is compatible only with CO2SYSv3. 41 | 42 | ## CITATION 43 | 44 | The full citation for CO2SYSv3 (Sharp et al., 2023) is given below. Cite this version if using CO2SYSv3 for CO2 system calculations or propagating errors in CO2 system calculations using the extended errors.m or derivnum.m routines provided here. 45 | 46 | If using any CO2SYS program for CO2 system calculations, cite also the original CO2SYS DOS work of Lewis and Wallace (1998). 47 | 48 | If using the CO2SYS MATLAB program for CO2 system calculations, cite also the work of van Heuven et al. (2011). 49 | 50 | If using the derivnum.m and/or errors.m programs for CO2 system error propagations, cite also the work of Orr et al. (2018). 51 | 52 | ## REFERENCES 53 | 54 | Bach, L. T. (2015). Reconsidering the role of carbonate ion concentration in calcification by marine organisms. Biogeosciences 12(16), 4939–4951. 55 | 56 | Dillon, W. D. N., Dillingham, P. W., Currie, K. I., McGraw, C. M., 2020. Inclusion of uncertainty in the calcium-salinity relationship improves estimates of ocean acidification monitoring data quality. Marine Chemistry 226, 103872. 57 | 58 | Humphreys, M.P., Lewis, E.R., Sharp, J.D., Pierrot, D. (2022). PyCO2SYS: marine carbonate system calculations in Python. Geoscientific Model Development 15, 15-43.. 59 | 60 | Lewis, E., Wallace, D. W. R., 1998. Program Developed for CO2 System Calculations. ORNL/CDIAC-105. Carbon Dioxide Information Analysis Center, Oak Ridge National Laboratory, Oak Ridge, TN. 61 | 62 | Munhoven, G., Mathematics of the total alkalinity–pH equation – pathway to robust and universal solution algorithms: the SolveSAPHE package v1.0.1. Geoscientific Model Development 6, 1367–1388, 2013 63 | 64 | Orr, J.C., Epitalon, J.-M., Dickson, A. G., Gattuso, J.-P., 2018. Routine uncertainty propagation for the marine carbon dioxide system. Marine Chemistry 207, 84-107. 65 | 66 | Pierrot, D. E. Lewis,and D. W. R. Wallace. 2006. MS Excel Program Developed for CO2 System Calculations. ORNL/CDIAC-105a. Carbon Dioxide Information Analysis Center, Oak Ridge National Laboratory, U.S. Department of Energy, Oak Ridge, Tennessee. 67 | 68 | Sharp, J.D., Pierrot, D., Humphreys, M.P., Epitalon, J.-M., Orr, J.C., Lewis, E.R., Wallace, D.W.R. (2023, Jan. 19). CO2SYSv3 for MATLAB (Version v3.2.1). Zenodo. http://doi.org/10.5281/zenodo.3950562 69 | 70 | Sulpis, O., Lauvset, S. K., and Hagens, M., 2020. Current estimates of K1* and K2* appear inconsistent with measured CO2 system parameters in cold oceanic regions. Ocean Science Discussions, 1-27. 71 | 72 | van Heuven, S., Pierrot, D., Rae, J.W.B., Lewis, E., Wallace, D.W.R., 2011. MATLAB Program Developed for CO2 System Calculations. ORNL/CDIAC-105b. Carbon Dioxide Information Analysis Center, Oak Ridge National Laboratory, Oak Ridge, TN. 73 | -------------------------------------------------------------------------------- /comparisons/CO2SYSigen.m: -------------------------------------------------------------------------------- 1 | function [PAR1, PAR2, PAR1TYPE, PAR2TYPE, SAL, pHSCALEIN, ... 2 | K1K2CONSTANTS, KSO4CONSTANTS, KSO4CONSTANT, KFCONSTANT, BORON, ... 3 | UNCER1, UNCER2] = ... 4 | CO2SYSigen(PARvalues, PARTYPEs, SALvalue, pHSCALEIN_opts, ... 5 | K1K2CONSTANTS_opts, KSO4CONSTANTS_opts, KFCONSTANT_opts) 6 | %CO2SYSigen Generate all possible combinations of input parameters to test 7 | % CO2SYS functions // Matthew P. Humphreys [2020-03-19] 8 | % 9 | % JD Sharp added parameter uncertainy values as output 10 | 11 | %% Set example inputs 12 | % PARvalues = fliplr([2300 2000 8.05 400 405]); 13 | % PARTYPEs = 5:-1:1; 14 | % if numel(PARvalues) ~= numel(PARTYPEs) 15 | % disp('PARvalues and PARTYPEs must be the same size!') 16 | % return 17 | % end % if 18 | % pHSCALEIN_opts = 1:4; 19 | % K1K2CONSTANTS_opts = 1:14; 20 | % KSO4CONSTANTS_opts = 1:4; 21 | % KFCONSTANT_opts = 1:2; 22 | % SALvalue = 33.1; 23 | 24 | % add uncertainty values 25 | UNCER = nan(size(PARTYPEs)); 26 | UNCER(PARTYPEs==1) = 2; 27 | UNCER(PARTYPEs==2) = 2; 28 | UNCER(PARTYPEs==3) = 0.01; 29 | UNCER(PARTYPEs==4) = 2; 30 | UNCER(PARTYPEs==5) = 2; 31 | 32 | %% Get all valid PAR combinations 33 | PAR12ixs_all = combnk(1:numel(PARvalues), 2); 34 | PAR12combos = PARTYPEs(PAR12ixs_all); 35 | validcombos = ~( ... 36 | (PAR12combos(:, 1) == 4 & PAR12combos(:, 2) == 5) | ... 37 | (PAR12combos(:, 1) == 5 & PAR12combos(:, 2) == 4) | ... 38 | (PAR12combos(:, 1) == 4 & PAR12combos(:, 2) == 8) | ... 39 | (PAR12combos(:, 1) == 8 & PAR12combos(:, 2) == 4) | ... 40 | (PAR12combos(:, 1) == 5 & PAR12combos(:, 2) == 8) | ... 41 | (PAR12combos(:, 1) == 8 & PAR12combos(:, 2) == 5)); 42 | PAR12ixs = PAR12ixs_all(validcombos, :); 43 | PAR12ixs = [PAR12ixs; fliplr(PAR12ixs)]; 44 | ncombos = size(PAR12ixs, 1); 45 | 46 | % Count all possible combinations of other inputs 47 | nopts = ... 48 | numel(pHSCALEIN_opts)* ... 49 | numel(K1K2CONSTANTS_opts)* ... 50 | numel(KSO4CONSTANTS_opts)* ... 51 | numel(KFCONSTANT_opts); 52 | 53 | % Get corresponding PAR combinations 54 | PAR12ixs = repmat(PAR12ixs, nopts, 1); 55 | PARsin = PARvalues(PAR12ixs); 56 | UNCERin = UNCER(PAR12ixs); 57 | PAR1 = PARsin(:, 1); 58 | PAR2 = PARsin(:, 2); 59 | UNCER1 = UNCERin(:,1); 60 | UNCER2 = UNCERin(:,2); 61 | PAR12combos = PARTYPEs(PAR12ixs); 62 | PAR1TYPE = PAR12combos(:, 1); 63 | PAR2TYPE = PAR12combos(:, 2); 64 | 65 | % Grid and reshape corresponding other inputs 66 | [pHSCALEIN, K1K2CONSTANTS, KSO4CONSTANTS, KFCONSTANT] = ... 67 | ndgrid( ... 68 | pHSCALEIN_opts, ... 69 | K1K2CONSTANTS_opts, ... 70 | KSO4CONSTANTS_opts, ... 71 | KFCONSTANT_opts); 72 | pHSCALEIN = repmat(pHSCALEIN(:), 1, ncombos)'; 73 | pHSCALEIN = pHSCALEIN(:); 74 | K1K2CONSTANTS = repmat(K1K2CONSTANTS(:), 1, ncombos)'; 75 | K1K2CONSTANTS = K1K2CONSTANTS(:); 76 | KSO4CONSTANTS = repmat(KSO4CONSTANTS(:), 1, ncombos)'; 77 | KSO4CONSTANTS = KSO4CONSTANTS(:); 78 | KFCONSTANT = repmat(KFCONSTANT(:), 1, ncombos)'; 79 | KFCONSTANT = KFCONSTANT(:); 80 | 81 | % Get new-style KSO4CONSTANT and BORON inputs from KSO4CONSTANTS 82 | both2KSO4 = [1 2 1 2]; 83 | KSO4CONSTANT = both2KSO4(KSO4CONSTANTS)'; 84 | both2BSal = [1 1 2 2]; 85 | BORON = both2BSal(KSO4CONSTANTS)'; 86 | 87 | % Zero out salinities for freshwater K1K2CONSTANTS option 88 | SAL = SALvalue*ones(size(K1K2CONSTANTS)); 89 | SAL(K1K2CONSTANTS == 8) = 0; 90 | -------------------------------------------------------------------------------- /comparisons/calculate_derivatives.m: -------------------------------------------------------------------------------- 1 | % Generate derivatives across set of input conditions using CO2SYSv3.2.0 2 | 3 | %% Set up input conditions 4 | PARvalues = [2250 2100 8.1 400 405]; 5 | PARTYPEs = 1:5; 6 | pHSCALEIN_opts = 1:4; 7 | K1K2CONSTANTS_opts = 1:15; 8 | KSO4CONSTANTS_opts = 1:4; 9 | KFCONSTANT_opts = 1; 10 | SALvalue = 33.1; 11 | [P1, P2, P1type, P2type, sal, pHscales, K1K2, KSO4_only, KSO4, KF, ... 12 | BSal, U1, U2] = CO2SYSigen(PARvalues, PARTYPEs, SALvalue, pHSCALEIN_opts, ... 13 | K1K2CONSTANTS_opts, KSO4CONSTANTS_opts, KFCONSTANT_opts); 14 | tempin = 24; 15 | tempout = 12; 16 | presin = 1; 17 | presout = 1647; 18 | si = 10; 19 | phos = 1; 20 | 21 | %% Determine whether to calculate each input row or not 22 | % xrow = 1 + 210; % just do one row, or... 23 | xrow = 1:numel(P1); % ... do all rows (do this for saving output file) 24 | P1 = P1(xrow); 25 | P2 = P2(xrow); 26 | P1type = P1type(xrow); 27 | P2type = P2type(xrow); 28 | sal = sal(xrow); 29 | pHscales = pHscales(xrow); 30 | K1K2 = K1K2(xrow); 31 | KSO4_only = KSO4_only(xrow); 32 | 33 | %% Run derivatives using CO2SYSv3 34 | disp('Running derivnum v3...') 35 | tic 36 | [DERIV_PAR1_v3, HEADERS_PAR1_v3] = ... 37 | derivnum('PAR1', P1, P2, P1type, P2type, sal, tempin, tempout, presin, ... 38 | presout, si, phos, 0, 0, pHscales, K1K2, KSO4, KF, BSal); 39 | [DERIV_PAR2_v3, HEADERS_PAR2_v3] = ... 40 | derivnum('PAR2', P1, P2, P1type, P2type, sal, tempin, tempout, presin, ... 41 | presout, si, phos, 0, 0, pHscales, K1K2, KSO4, KF, BSal); 42 | [DERIV_TEMP_v3, HEADERS_TEMP_v3] = ... 43 | derivnum('t', P1, P2, P1type, P2type, sal, tempin, tempout, presin, ... 44 | presout, si, phos, 0, 0, pHscales, K1K2, KSO4, KF, BSal); 45 | [DERIV_SAL_v3, HEADERS_SAL_v3] = ... 46 | derivnum('s', P1, P2, P1type, P2type, sal, tempin, tempout, presin, ... 47 | presout, si, phos, 0, 0, pHscales, K1K2, KSO4, KF, BSal); 48 | [DERIV_SIL_v3, HEADERS_SIL_v3] = ... 49 | derivnum('sil', P1, P2, P1type, P2type, sal, tempin, tempout, presin, ... 50 | presout, si, phos, 0, 0, pHscales, K1K2, KSO4, KF, BSal); 51 | [DERIV_PHOS_v3, HEADERS_PHOS_v3] = ... 52 | derivnum('phos', P1, P2, P1type, P2type, sal, tempin, tempout, presin, ... 53 | presout, si, phos, 0, 0, pHscales, K1K2, KSO4, KF, BSal); 54 | [DERIV_K0_v3, HEADERS_K0_v3] = ... 55 | derivnum('k0', P1, P2, P1type, P2type, sal, tempin, tempout, presin, ... 56 | presout, si, phos, 0, 0, pHscales, K1K2, KSO4, KF, BSal); 57 | [DERIV_K1_v3, HEADERS_K1_v3] = ... 58 | derivnum('k1', P1, P2, P1type, P2type, sal, tempin, tempout, presin, ... 59 | presout, si, phos, 0, 0, pHscales, K1K2, KSO4, KF, BSal); 60 | [DERIV_K2_v3, HEADERS_K2_v3] = ... 61 | derivnum('k2', P1, P2, P1type, P2type, sal, tempin, tempout, presin, ... 62 | presout, si, phos, 0, 0, pHscales, K1K2, KSO4, KF, BSal); 63 | [DERIV_KB_v3, HEADERS_KB_v3] = ... 64 | derivnum('kb', P1, P2, P1type, P2type, sal, tempin, tempout, presin, ... 65 | presout, si, phos, 0, 0, pHscales, K1K2, KSO4, KF, BSal); 66 | [DERIV_KW_v3, HEADERS_KW_v3] = ... 67 | derivnum('kw', P1, P2, P1type, P2type, sal, tempin, tempout, presin, ... 68 | presout, si, phos, 0, 0, pHscales, K1K2, KSO4, KF, BSal); 69 | [DERIV_BOR_v3, HEADERS_BOR_v3] = ... 70 | derivnum('bor', P1, P2, P1type, P2type, sal, tempin, tempout, presin, ... 71 | presout, si, phos, 0, 0, pHscales, K1K2, KSO4, KF, BSal); 72 | toc 73 | 74 | %% Put results in tables 75 | clear par1_v3 76 | HEADERS_PAR1_v3 = strrep(HEADERS_PAR1_v3,'<',''); 77 | HEADERS_PAR1_v3 = strrep(HEADERS_PAR1_v3,'>',''); 78 | HEADERS_PAR1_v3 = strrep(HEADERS_PAR1_v3,'/','_'); 79 | for V = 1:numel(HEADERS_PAR1_v3) 80 | par1_v3.(HEADERS_PAR1_v3{V}) = DERIV_PAR1_v3(:, V); 81 | end 82 | par1_v3 = struct2table(par1_v3); 83 | clear par2_v3 84 | HEADERS_PAR2_v3 = strrep(HEADERS_PAR2_v3,'<',''); 85 | HEADERS_PAR2_v3 = strrep(HEADERS_PAR2_v3,'>',''); 86 | HEADERS_PAR2_v3 = strrep(HEADERS_PAR2_v3,'/','_'); 87 | for V = 1:numel(HEADERS_PAR2_v3) 88 | par2_v3.(HEADERS_PAR2_v3{V}) = DERIV_PAR2_v3(:, V); 89 | end 90 | par2_v3 = struct2table(par2_v3); 91 | clear temp_v3 92 | HEADERS_TEMP_v3 = strrep(HEADERS_TEMP_v3,'<',''); 93 | HEADERS_TEMP_v3 = strrep(HEADERS_TEMP_v3,'>',''); 94 | HEADERS_TEMP_v3 = strrep(HEADERS_TEMP_v3,'/','_'); 95 | for V = 1:numel(HEADERS_TEMP_v3) 96 | temp_v3.(HEADERS_TEMP_v3{V}) = DERIV_TEMP_v3(:, V); 97 | end 98 | temp_v3 = struct2table(temp_v3); 99 | clear sal_v3 100 | HEADERS_SAL_v3 = strrep(HEADERS_SAL_v3,'<',''); 101 | HEADERS_SAL_v3 = strrep(HEADERS_SAL_v3,'>',''); 102 | HEADERS_SAL_v3 = strrep(HEADERS_SAL_v3,'/','_'); 103 | for V = 1:numel(HEADERS_SAL_v3) 104 | sal_v3.(HEADERS_SAL_v3{V}) = DERIV_SAL_v3(:, V); 105 | end 106 | sal_v3 = struct2table(sal_v3); 107 | clear sil_v3 108 | HEADERS_SIL_v3 = strrep(HEADERS_SIL_v3,'<',''); 109 | HEADERS_SIL_v3 = strrep(HEADERS_SIL_v3,'>',''); 110 | HEADERS_SIL_v3 = strrep(HEADERS_SIL_v3,'/','_'); 111 | for V = 1:numel(HEADERS_SIL_v3) 112 | sil_v3.(HEADERS_SIL_v3{V}) = DERIV_SIL_v3(:, V); 113 | end 114 | sil_v3 = struct2table(sil_v3); 115 | clear phos_v3 116 | HEADERS_PHOS_v3 = strrep(HEADERS_PHOS_v3,'<',''); 117 | HEADERS_PHOS_v3 = strrep(HEADERS_PHOS_v3,'>',''); 118 | HEADERS_PHOS_v3 = strrep(HEADERS_PHOS_v3,'/','_'); 119 | for V = 1:numel(HEADERS_PHOS_v3) 120 | phos_v3.(HEADERS_PHOS_v3{V}) = DERIV_PHOS_v3(:, V); 121 | end 122 | phos_v3 = struct2table(phos_v3); 123 | clear k0_v3 124 | HEADERS_K0_v3 = strrep(HEADERS_K0_v3,'<',''); 125 | HEADERS_K0_v3 = strrep(HEADERS_K0_v3,'>',''); 126 | HEADERS_K0_v3 = strrep(HEADERS_K0_v3,'/','_'); 127 | for V = 1:numel(HEADERS_K0_v3) 128 | k0_v3.(HEADERS_K0_v3{V}) = DERIV_K0_v3(:, V); 129 | end 130 | k0_v3 = struct2table(k0_v3); 131 | clear k1_v3 132 | HEADERS_K1_v3 = strrep(HEADERS_K1_v3,'<',''); 133 | HEADERS_K1_v3 = strrep(HEADERS_K1_v3,'>',''); 134 | HEADERS_K1_v3 = strrep(HEADERS_K1_v3,'/','_'); 135 | for V = 1:numel(HEADERS_K1_v3) 136 | k1_v3.(HEADERS_K1_v3{V}) = DERIV_K1_v3(:, V); 137 | end 138 | k1_v3 = struct2table(k1_v3); 139 | clear k2_v3 140 | HEADERS_K2_v3 = strrep(HEADERS_K2_v3,'<',''); 141 | HEADERS_K2_v3 = strrep(HEADERS_K2_v3,'>',''); 142 | HEADERS_K2_v3 = strrep(HEADERS_K2_v3,'/','_'); 143 | for V = 1:numel(HEADERS_K2_v3) 144 | k2_v3.(HEADERS_K2_v3{V}) = DERIV_K2_v3(:, V); 145 | end 146 | k2_v3 = struct2table(k2_v3); 147 | clear kb_v3 148 | HEADERS_KB_v3 = strrep(HEADERS_KB_v3,'<',''); 149 | HEADERS_KB_v3 = strrep(HEADERS_KB_v3,'>',''); 150 | HEADERS_KB_v3 = strrep(HEADERS_KB_v3,'/','_'); 151 | for V = 1:numel(HEADERS_KB_v3) 152 | kb_v3.(HEADERS_KB_v3{V}) = DERIV_KB_v3(:, V); 153 | end 154 | kb_v3 = struct2table(kb_v3); 155 | clear kw_v3 156 | HEADERS_KW_v3 = strrep(HEADERS_KW_v3,'<',''); 157 | HEADERS_KW_v3 = strrep(HEADERS_KW_v3,'>',''); 158 | HEADERS_KW_v3 = strrep(HEADERS_KW_v3,'/','_'); 159 | for V = 1:numel(HEADERS_KW_v3) 160 | kw_v3.(HEADERS_KW_v3{V}) = DERIV_KW_v3(:, V); 161 | end 162 | kw_v3 = struct2table(kw_v3); 163 | clear bor_v3 164 | HEADERS_BOR_v3 = strrep(HEADERS_BOR_v3,'<',''); 165 | HEADERS_BOR_v3 = strrep(HEADERS_BOR_v3,'>',''); 166 | HEADERS_BOR_v3 = strrep(HEADERS_BOR_v3,'/','_'); 167 | for V = 1:numel(HEADERS_BOR_v3) 168 | bor_v3.(HEADERS_BOR_v3{V}) = DERIV_BOR_v3(:, V); 169 | end 170 | bor_v3 = struct2table(bor_v3); 171 | 172 | writetable(par1_v3,'derivatives/par1_v3.csv') 173 | writetable(par2_v3,'derivatives/par2_v3.csv') 174 | writetable(temp_v3,'derivatives/temp_v3.csv') 175 | writetable(sal_v3,'derivatives/sal_v3.csv') 176 | writetable(sil_v3,'derivatives/sil_v3.csv') 177 | writetable(phos_v3,'derivatives/phos_v3.csv') 178 | writetable(k0_v3,'derivatives/k0_v3.csv') 179 | writetable(k1_v3,'derivatives/k1_v3.csv') 180 | writetable(k2_v3,'derivatives/k2_v3.csv') 181 | writetable(kb_v3,'derivatives/kb_v3.csv') 182 | writetable(kw_v3,'derivatives/kw_v3.csv') 183 | writetable(bor_v3,'derivatives/bor_v3.csv') 184 | -------------------------------------------------------------------------------- /comparisons/compare_to_Orr_et_al_2018.m: -------------------------------------------------------------------------------- 1 | %% Determine path 2 | fpath = mfilename('fullpath'); 3 | path = fileparts(fpath) ; 4 | 5 | %% Reproduce values in Table 2 of Orr et al., 2018 6 | % Calculate derivatives 7 | ders = derivnum_adjusted_to_v2_0_5('PAR1',2300,2000,1,2,35,18,NaN,0,NaN,0,0,0,0,1,10,1,1,2); 8 | dv2(1,1:6) = ders([3 4 8 6 7 11]); 9 | ders = derivnum_adjusted_to_v2_0_5('PAR2',2300,2000,1,2,35,18,NaN,0,NaN,0,0,0,0,1,10,1,1,2); 10 | dv2(2,1:6) = ders([3 4 8 6 7 11]); 11 | ders = derivnum_adjusted_to_v2_0_5('t',2300,2000,1,2,35,18,NaN,0,NaN,0,0,0,0,1,10,1,1,2); 12 | dv2(3,1:6) = ders([3 4 8 6 7 11]); 13 | ders = derivnum_adjusted_to_v2_0_5('s',2300,2000,1,2,35,18,NaN,0,NaN,0,0,0,0,1,10,1,1,2); 14 | dv2(4,1:6) = ders([3 4 8 6 7 11]); 15 | % Load Table 2 16 | tab2 = readtable(strcat(path,'/data/orr2018-table2.csv')); 17 | tab2 = table2array(tab2(:,3:end)); 18 | tab2 = tab2([1 7 13 17],:); 19 | % Calculate differences 20 | table2_differences = tab2-dv2 21 | 22 | %% Reproduce values in Table 3 of Orr et al., 2018 23 | % Calculate derivatives 24 | ders = derivnum_adjusted_to_v2_0_5('phos',2300,2000,1,2,35,18,NaN,0,NaN,60,2,0,0,1,10,1,1,2); 25 | dv3(1,1:6) = ders([3 4 8 6 7 11]); 26 | ders = derivnum_adjusted_to_v2_0_5('sil',2300,2000,1,2,35,18,NaN,0,NaN,60,2,0,0,1,10,1,1,2); 27 | dv3(2,1:6) = ders([3 4 8 6 7 11]); 28 | % Load Table 3 29 | tab3 = readtable(strcat(path,'/data/orr2018-table3.csv')); 30 | tab3 = table2array(tab3(:,3:end)); 31 | tab3 = tab3([1 5],:); 32 | % Calculate differences 33 | table3_differences = tab3-dv3 34 | 35 | %% Reproduce values in Table 4 of Orr et al., 2018 36 | errs = errors_adjusted_to_v2_0_5(2300,2000,1,2,35,18,NaN,0,NaN,60,2,0,0,2,2,... 37 | 0,0,4,0.1,0,0,[0 0 0 0 0 0 0],0,0,1,10,1,1,1); 38 | err4(1,1:8) = errs([3 8 5 4 6 7 11 10]); 39 | errs = errors_adjusted_to_v2_0_5(2300,2000,1,2,35,18,NaN,0,NaN,60,2,0,0,2,2,... 40 | 0,0,4,0.1,0,0,[0.002 0.0075 0.015 0.01 0.01 0.02 0.02],0.02,0,1,10,1,1,1); 41 | err4(2,1:8) = errs([3 8 5 4 6 7 11 10]); 42 | % Load Table 3 43 | tab4 = readtable(strcat(path,'/data/orr2018-table4.csv')); 44 | tab4 = table2array(tab4(:,4:end)); 45 | tab4 = tab4([1 5],:); 46 | % Calculate differences 47 | table4_differences = tab4-err4 48 | -------------------------------------------------------------------------------- /comparisons/compare_versions.m: -------------------------------------------------------------------------------- 1 | % Compares CO2SYS v3.2.0 with CO2SYS v2.0.5. 2 | % 3 | % CO2SYS v2.0.5 comes from https://github.com/jamesorr/CO2SYS-MATLAB, 4 | % but you must first rename the function to CO2SYSv2_0_5 (both inside the 5 | % file and in the file name). 6 | % 7 | % CO2SYS v3.2.0 comes from 8 | % https://github.com/jonathansharp/CO2-System-Extd/v2_0_5_compatible 9 | % 10 | % CO2SYSigen comes from 11 | % https://github.com/jonathansharp/CO2-System-Extd/comparisons/CO2SYSigen.m, 12 | % 13 | % compare_versions from 14 | % https://github.com/jonathansharp/CO2-System-Extd/comparisons/compare_versions.m 15 | % 16 | % Corrctions for KSO4, KF, and BSal inputs and column 17 | % headers from J. Sharp, 10 June 2020 18 | % 19 | % Differences are expected in outputs between the two versions due to minor 20 | % differences in the way [CO2] is determined and a slight error in 21 | % CO2SYSv2.0.5 in calculating Revelle factor 22 | 23 | %% Set up input conditions 24 | PARvalues = [2250 2100 8.1 400 405]; 25 | PARTYPEs = 1:5; 26 | pHSCALEIN_opts = 1:4; 27 | K1K2CONSTANTS_opts = 1:15; 28 | KSO4CONSTANTS_opts = 1:4; 29 | KFCONSTANT_opts = 1; 30 | SALvalue = 33.1; 31 | [P1, P2, P1type, P2type, sal, pHscales, K1K2, KSO4_only, KSO4, KF, ... 32 | BSal, U1, U2] = CO2SYSigen(PARvalues, PARTYPEs, SALvalue, pHSCALEIN_opts, ... 33 | K1K2CONSTANTS_opts, KSO4CONSTANTS_opts, KFCONSTANT_opts); 34 | tempin = 24; 35 | tempout = 12; 36 | presin = 1; 37 | presout = 1647; 38 | si = 10; 39 | phos = 1; 40 | 41 | %% Determine whether to calculate each input row or not 42 | % xrow = 1 + 210; % just do one row, or... 43 | xrow = 1:numel(P1); % ... do all rows (do this for saving output file) 44 | P1 = P1(xrow); 45 | P2 = P2(xrow); 46 | P1type = P1type(xrow); 47 | P2type = P2type(xrow); 48 | sal = sal(xrow); 49 | pHscales = pHscales(xrow); 50 | K1K2 = K1K2(xrow); 51 | KSO4_only = KSO4_only(xrow); 52 | 53 | %% Run CO2SYSv2.0.5 54 | disp('Running CO2SYS v2.0.5...') 55 | tic 56 | [DATA_v2, HEADERS_v2] = ... 57 | CO2SYSv2_0_5(P1, P2, P1type, P2type, sal, tempin, tempout, presin, ... 58 | presout, si, phos, pHscales, K1K2, KSO4_only); 59 | toc 60 | 61 | %% Run CO2SYSv3 62 | disp('Running CO2SYS v3...') 63 | tic 64 | [DATA_v3, HEADERS_v3] = ... 65 | CO2SYS_adjusted_to_v2_0_5(P1, P2, P1type, P2type, sal, tempin, tempout, presin, ... 66 | presout, si, phos, 0, 0, pHscales, K1K2, KSO4, KF, BSal); 67 | toc 68 | 69 | %% Put results in tables 70 | clear co2s_v2 71 | for V = 1:numel(HEADERS_v2) 72 | co2s_v2.(HEADERS_v2{V}) = DATA_v2(:, V); 73 | end 74 | co2s_v2 = struct2table(co2s_v2); 75 | clear co2s_v3 76 | for V = 1:numel(HEADERS_v3) 77 | co2s_v3.(HEADERS_v3{V}) = DATA_v3(:, V); 78 | end 79 | co2s_v3 = struct2table(co2s_v3); 80 | 81 | %% Calculate differences 82 | clear co2s_diff 83 | H=1; 84 | for V = 1:numel(HEADERS_v3) 85 | if H < numel(HEADERS_v2) 86 | if isequal(HEADERS_v3{V},HEADERS_v2{H}) 87 | co2s_diff.(HEADERS_v2{H}) = ... 88 | abs((co2s_v3.(HEADERS_v3{V}) - co2s_v2.(HEADERS_v2{H})) ./ co2s_v2.(HEADERS_v2{H})).*100; 89 | elseif isequal(HEADERS_v2{H},'KSO4CONSTANTS') && isequal(HEADERS_v3{V},'KSO4CONSTANT') 90 | co2s_diff.(HEADERS_v2{H}) = ... 91 | abs((co2s_v3.(HEADERS_v3{V}) - co2s_v2.(HEADERS_v2{H})) ./ co2s_v2.(HEADERS_v2{H})).*100; 92 | else 93 | H = H-1; 94 | end 95 | end 96 | H = H+1; 97 | end % for V 98 | co2s_diff = struct2table(co2s_diff); 99 | -------------------------------------------------------------------------------- /comparisons/compare_versions_uncert.m: -------------------------------------------------------------------------------- 1 | % Compares CO2SYS v3 with CO2SYS v2.0.5. 2 | % 3 | % errors v2.0.5 comes from https://github.com/jamesorr/CO2SYS-MATLAB, 4 | % but you must first rename the function to errorsv2_0_5 (both inside the 5 | % file and in the file name), and you must make a change in line 485 of 6 | % CO2SYSv2.0.5: case {'KW'} should be changed to case {'BOR'} 7 | % 8 | % errors v3 comes from 9 | % https://github.com/jonathansharp/CO2-System-Extd/v2_0_5_compatible 10 | % 11 | % CO2SYSigen comes from 12 | % https://github.com/jonathansharp/CO2-System-Extd/comparisons/CO2SYSigen.m, 13 | % 14 | % compare_versions_uncert from 15 | % https://github.com/jonathansharp/CO2-System-Extd/comparisons/compare_versions.m 16 | 17 | %% Add tools to path (if you need to!) 18 | % addpath('/home/matthew/github/PyCO2SYS/validate') 19 | 20 | %% Set up input conditions 21 | PARvalues = [2250 2100 8.1 400 405]; 22 | PARTYPEs = 1:5; 23 | pHSCALEIN_opts = 1:4; 24 | K1K2CONSTANTS_opts = 1:15; 25 | KSO4CONSTANTS_opts = 1:4; 26 | KFCONSTANT_opts = 1; 27 | SALvalue = 33.1; 28 | [P1, P2, P1type, P2type, sal, pHscales, K1K2, KSO4_only, KSO4, KF, ... 29 | BSal, U1, U2] = CO2SYSigen(PARvalues, PARTYPEs, SALvalue, pHSCALEIN_opts, ... 30 | K1K2CONSTANTS_opts, KSO4CONSTANTS_opts, KFCONSTANT_opts); 31 | tempin = 24; 32 | tempout = 12; 33 | presin = 1; 34 | presout = 1647; 35 | si = 10; 36 | phos = 1; 37 | 38 | %% Determine whether to calculate each input row or not 39 | % xrow = 1 + 210; % just do one row, or... 40 | xrow = 1:numel(P1); % ... do all rows (do this for saving output file) 41 | P1 = P1(xrow); 42 | P2 = P2(xrow); 43 | P1type = P1type(xrow); 44 | P2type = P2type(xrow); 45 | sal = sal(xrow); 46 | pHscales = pHscales(xrow); 47 | K1K2 = K1K2(xrow); 48 | KSO4_only = KSO4_only(xrow); 49 | 50 | %% Run uncertainties using CO2SYSv2.0.5 **SLOWWWWWW** 51 | % Define dissociation constant uncertainties 52 | epK = [0.002, 0.0075, 0.015, 0.01, 0.01, 0.02, 0.02]; 53 | disp('Running errors.m (CO2SYS v2.0.5)...') 54 | tic 55 | ERR_v2 = nan(size(P1,1),20); 56 | ERR_HEADERS_v2 = cell(size(P1,1),20); 57 | UNITS_v2 = cell(size(P1,1),20); 58 | for n = 1:size(P1,1) 59 | [err, head, units] = ... 60 | errorsv2_0_5(P1(n), P2(n), P1type(n), P2type(n), sal(n), tempin, tempout, presin, ... 61 | presout, si, phos, U1(n), U2(n), 0.01, 0.02, 0.1, 0.01, epK, 0.02, 0.1, ... 62 | pHscales(n), K1K2(n), KSO4_only(n)); 63 | ERR_v2(n,:) = err; 64 | ERR_HEADERS_v2 = head; 65 | UNITS_v2 = units; 66 | end 67 | toc 68 | 69 | %% Run uncertainties using CO2SYSv3 70 | epK = [0.002, 0.0075, 0.015, 0.01, 0.01, 0.02, 0.02]; 71 | disp('Running errors.m (CO2SYS v3)...') 72 | tic 73 | [ERR_v3, ERR_HEADERS_v3, UNITS_v3] = ... 74 | errors_adjusted_to_v2_0_5(P1, P2, P1type, P2type, sal, tempin, tempout, presin, ... 75 | presout, si, phos, 0, 0, U1, U2, 0.01, 0.02, 0.1, 0.01, 0, 0, epK, ... 76 | 0.02, 0.1, pHscales, K1K2, KSO4, KF, BSal, 0); 77 | toc 78 | 79 | %% Put results in tables 80 | clear errs_v2 81 | ERR_HEADERS_v2 = strrep(ERR_HEADERS_v2,'(','_'); 82 | ERR_HEADERS_v2 = strrep(ERR_HEADERS_v2,')','_'); 83 | for V = 1:numel(ERR_HEADERS_v2) 84 | errs_v2.(ERR_HEADERS_v2{V}) = ERR_v2(:, V); 85 | end 86 | errs_v2 = struct2table(errs_v2); 87 | clear errs_v3 88 | ERR_HEADERS_v3 = strrep(ERR_HEADERS_v3,'(','_'); 89 | ERR_HEADERS_v3 = strrep(ERR_HEADERS_v3,')','_'); 90 | for V = 1:numel(ERR_HEADERS_v3) 91 | errs_v3.(ERR_HEADERS_v3{V}) = ERR_v3(:, V); 92 | end 93 | errs_v3 = struct2table(errs_v3); 94 | 95 | %% Calculate differences in errors 96 | clear errs_diff 97 | H=1; 98 | for V = 1:8 99 | errs_diff.(ERR_HEADERS_v3{V}) = abs((errs_v3.(ERR_HEADERS_v3{V}) - ... 100 | errs_v2.(ERR_HEADERS_v2{V})) ./ errs_v2.(ERR_HEADERS_v2{V})).*100; 101 | end 102 | for V = 9:17 103 | errs_diff.(ERR_HEADERS_v3{V+1}) = abs((errs_v3.(ERR_HEADERS_v3{V+1}) - ... 104 | errs_v2.(ERR_HEADERS_v2{V})) ./ errs_v2.(ERR_HEADERS_v2{V})).*100; 105 | end 106 | for V = 18:20 107 | errs_diff.(ERR_HEADERS_v3{V+2}) = abs((errs_v3.(ERR_HEADERS_v3{V+2}) - ... 108 | errs_v2.(ERR_HEADERS_v2{V})) ./ errs_v2.(ERR_HEADERS_v2{V})).*100; 109 | end 110 | errs_diff = struct2table(errs_diff); 111 | -------------------------------------------------------------------------------- /comparisons/data/orr2018-table2.csv: -------------------------------------------------------------------------------- 1 | wrt,program,Hfree,pCO2,aqueous_CO2,bicarbonate,carbonate,saturation_aragonite 2 | alkalinity,CO2SYS,-0.02558196,-1.192136,-0.04073334,-0.6310411,0.6717745,0.01037201 3 | alkalinity,mocsy,-0.02558194,-1.192138,-0.04073333,-0.6310401,0.6717725,0.01037431 4 | alkalinity,ref_d,-0.02558194,-1.192138,-0.04073333,-0.6310401,0.6717725,0.01037431 5 | alkalinity,ref_e,-0.02558201,-1.192140,-0.04073345,-0.6310539,0.6717729,0.01037431 6 | alkalinity,seacarb,-0.02557970,-1.192033,-0.04072973,-0.6310761,0.6718058,0.01037482 7 | alkalinity,ref_f,-0.02558003,-1.192046,-0.04073025,-0.6310842,0.6718144,0.01037496 8 | dic,CO2SYS,0.02810982,1.461280,0.04992956,1.584240,-0.6341699,-0.00979141 9 | dic,mocsy,0.02810980,1.461283,0.04992955,1.584239,-0.6341677,-0.00979357 10 | dic,ref_d,0.02810980,1.461283,0.04992955,1.584239,-0.6341677,-0.00979357 11 | dic,ref_e,0.02810988,1.461285,0.04992968,1.584255,-0.6341683,-0.00979358 12 | dic,seacarb,0.02810754,1.461163,0.04992547,1.584277,-0.6342023,-0.00979411 13 | dic,ref_f,0.02810790,1.461178,0.04992604,1.584286,-0.6342118,-0.00979425 14 | temperature,CO2SYS,0.2525344,12.57638,0.1332509,-0.5848728,0.4516218,0.01716385 15 | temperature,mocsy,0.2525339,12.57640,0.1332499,-0.5848648,0.4516475,0.01716809 16 | temperature,ref_c,0.2525339,12.57640,0.1332499,-0.5848648,0.4516475,0.01716809 17 | temperature,seacarb,0.2525121,12.57521,0.1332345,-0.5849710,0.4517364,0.01717017 18 | salinity,CO2SYS,0.2099016,8.299964,0.2268601,1.004573,-1.231433,-0.03713724 19 | salinity,mocsy,0.2099018,8.299996,0.2268604,1.004582,-1.231429,-0.03714548 20 | salinity,ref_d,0.2099018,8.299996,0.2268604,1.004582,-1.231429,-0.03714548 21 | salinity,seacarb,0.2098768,8.298895,0.2268276,1.004441,-1.231269,-0.03714426 22 | -------------------------------------------------------------------------------- /comparisons/data/orr2018-table3.csv: -------------------------------------------------------------------------------- 1 | wrt,program,Hfree,pCO2,aqueous_CO2,bicarbonate,carbonate,saturation_aragonite 2 | total_phosphate,CO2SYS,0.02935462,1.36813,0.04674678,0.7009685,-0.7477153,-0.01154451 3 | total_phosphate,mocsy,0.02935621,1.368207,0.04674931,0.7010073,-0.7477555,-0.01154773 4 | total_phosphate,ref_c,0.02935621,1.368207,0.04674931,0.7010073,-0.7477555,-0.01154773 5 | total_phosphate,seacarb,0.02935388,1.368098,0.04674557,0.7010528,-0.7477984,-0.01154839 6 | total_silicate,CO2SYS,0.001069476,0.04984502,0.001703124,0.02553836,-0.02724148,-0.0004206009 7 | total_silicate,mocsy,0.001069695,0.04985535,0.001703473,0.02554362,-0.02724705,-0.0004207814 8 | total_silicate,ref_c,0.001069695,0.04985535,0.001703473,0.02554362,-0.02724705,-0.0004207814 9 | total_silicate,seacarb,0.001069679,0.04985458,0.001703446,0.02554693,-0.02725038,-0.0004208327 10 | -------------------------------------------------------------------------------- /comparisons/data/orr2018-table4.csv: -------------------------------------------------------------------------------- 1 | wrt,program,with_k_uncertainties,Hfree,aqueous_CO2,fCO2,pCO2,bicarbonate,carbonate,saturation_aragonite,saturation_calcite 2 | dic_alkalinity,CO2SYS_excel,no,0.07668398,0.12995259,3.7900747,3.8032995,3.4135224,1.8539528,0.02862451,0.04426536 3 | dic_alkalinity,CO2SYS_matlab,no,0.07668398,0.12995259,3.7900747,3.8032994,3.4135224,1.8539528,0.02862451,0.04426536 4 | dic_alkalinity,mocsy,no,0.07669003,0.12996267,3.7903686,3.8036035,3.4135222,1.8539595,0.02863104,0.04427546 5 | dic_alkalinity,seacarb,no,0.07668414,0.12995262,3.7900755,3.8033093,3.4136038,1.8540405,0.0286323,0.04427739 6 | dic_alkalinity,CO2SYS_excel,yes,0.19429498,0.32911625,9.6994199,9.7332644,4.4247168,3.2612783,0.15578845,0.24091348 7 | dic_alkalinity,CO2SYS_matlab,yes,0.19429498,0.32911625,9.6994199,9.7332642,4.4247168,3.2612783,0.15578845,0.24091348 8 | dic_alkalinity,mocsy,yes,0.19430788,0.32913860,9.7000746,9.7339444,4.4247421,3.2613327,0.15581821,0.24095949 9 | dic_alkalinity,seacarb,yes,0.19429739,0.32911714,9.6994403,9.7333078,4.4247272,3.2612654,0.15582641,0.24097219 10 | -------------------------------------------------------------------------------- /main/TOTALS.m: -------------------------------------------------------------------------------- 1 | function [Salts,Salts_Headers] = TOTALS(CO2SYSDATA,CO2SYSHEADERS) 2 | %************************************************************************** 3 | % 4 | % This function calculates the total concentrations of conservative 5 | % constituents from salinity using output from CO2SYS. Only total 6 | % concentrations that are not output by the CO2SYS function are calculated. 7 | % 8 | % If HEADERS are provided, this function will work with output from any 9 | % version of CO2SYS. If HEADERS are not provided, this function will work 10 | % only with output from CO2SYS v3.0, found at: 11 | % https://github.com/jonathansharp/CO2-System-Extd 12 | % 13 | % Citations: 14 | % Relative mass fractions are taken from: 15 | % Millero, F.J., et al., 2008. Deep-Sea Research Part I 55, 50-72. 16 | % Based on work from: 17 | % Riley, J.P. and Tongudai, M., 1967. Chemical Geology 2, 263-269. 18 | % Morris, A.W. and Riley, J.P., 1966. Deep-Sea Research 13, 669-705. 19 | % Culkin, F. and Cox, R.A., 1966. Deep-Sea Research 13, 789-804. 20 | % Carpenter, J.H. and Manella, M.E., 1973. J. Geophys. Res. 78, 3621-26. 21 | % 22 | % Function written by Jonathan Sharp 23 | % University of South Florida 24 | % June 16, 2020 25 | % 26 | %************************************************************************** 27 | 28 | if exist('CO2SYSHEADERS','var') 29 | 30 | SALidx = find(strcmp(CO2SYSHEADERS,'SAL')); 31 | 32 | % Cations (mol/kg-SW): 33 | 34 | % Sodium: 35 | Na = 0.5564924./22.989769.*(CO2SYSDATA(:,SALidx)./1.80655); 36 | % Potassium: 37 | K = 0.0206000./39.0983.*(CO2SYSDATA(:,SALidx)./1.80655); 38 | % Magnesium: 39 | Mg = 0.0662600./24.305.*(CO2SYSDATA(:,SALidx)./1.80655); 40 | % Calcium: 41 | Ca = 0.0212700./40.087.*(CO2SYSDATA(:,SALidx)./1.80655); 42 | % Strontium: 43 | Sr = 0.0004100./87.62.*(CO2SYSDATA(:,SALidx)./1.80655); 44 | 45 | % Anions (mol/kg-SW): 46 | 47 | % Chloride: 48 | Cl = 0.9989041./35.453.*(CO2SYSDATA(:,SALidx)./1.80655); 49 | % Bromide: 50 | Br = 0.0034730./79.904.*(CO2SYSDATA(:,SALidx)./1.80655); 51 | 52 | else 53 | 54 | % Cations (mol/kg-SW): 55 | 56 | % Sodium: 57 | Na = 0.5564924./22.989769.*(CO2SYSDATA(:,56)./1.80655); 58 | % Potassium: 59 | K = 0.0206000./39.0983.*(CO2SYSDATA(:,56)./1.80655); 60 | % Magnesium: 61 | Mg = 0.0662600./24.305.*(CO2SYSDATA(:,56)./1.80655); 62 | % Calcium: 63 | Ca = 0.0212700./40.087.*(CO2SYSDATA(:,56)./1.80655); 64 | % Strontium: 65 | Sr = 0.0004100./87.62.*(CO2SYSDATA(:,56)./1.80655); 66 | 67 | % Anions (mol/kg-SW): 68 | 69 | % Chloride: 70 | Cl = 0.9989041./35.453.*(CO2SYSDATA(:,56)./1.80655); 71 | % Bromide: 72 | Br = 0.0034730./79.904.*(CO2SYSDATA(:,56)./1.80655); 73 | 74 | end 75 | 76 | Salts = [Na,K,Mg,Ca,Sr,Cl,Br]; 77 | Salts_Headers = {'Na+','K+','Mg2+','Ca2+','Sr2+','Cl-','Br-'}; 78 | 79 | end 80 | -------------------------------------------------------------------------------- /main/derivnum.m: -------------------------------------------------------------------------------- 1 | % derivnum() 2 | % This subroutine computes partial derivatives of output carbonate variables 3 | % with respect to input variables (two), plus nutrients (two), ammonium and hydrogen sulfide, 4 | % temperature and salinity, dissociation constants, and total boron. 5 | % 6 | % It uses central differences, introducing a small perturbation 7 | % (plus and minus of a delta) in one INPUT variable and computes the 8 | % resulting induced change in each OUTPUT variable 9 | % 10 | % This subroutine has been modified from its original version (Orr et al. 11 | % 2018) to allow for input variables of CO2, HCO3, and CO3, the inclusion 12 | % of ammonium and hydrogen sulfide, and compatibility with CO2SYS.m(v3) 13 | % 14 | % After numerical tests, the small PERTURBATION (delta) is chosen 15 | % as a fraction of a reference value as follows: 16 | % * 1.e-3 (0.1%) for the equilibrium constants and total boron 17 | % * 1.e-6 for the pair of CO2 system input variables (PAR1, PAR2) 18 | % * 1.e-4 for temperature and salinity 19 | % * 1.e-3 for total dissolved inorganic P and Si (PO4, SI) 20 | % 21 | %************************************************************************** 22 | % 23 | % **** SYNTAX: 24 | % [deriv, headers_der, units_der, headers_err, units_err]=... 25 | % derivnum(VARID,PAR1,PAR2,PAR1TYPE,PAR2TYPE,... 26 | % SAL,TEMPIN,TEMPOUT,PRESIN,PRESOUT,... 27 | % SI,PO4,NH4,H2S,pHSCALEIN,K1K2CONSTANTS,... 28 | % KSO4CONSTANT,KFCONSTANT,BORON) 29 | % 30 | % **** SYNTAX EXAMPLES: 31 | % [der, headers, units] = derivnum('par1',2400,2200,1,2,35,0,25,4200,0,15,1,0,0,1,4,1,1,1) 32 | % [der, headers, units] = derivnum('sit', 2400, 8,1,3,35,0,25,4200,0,15,1,0,0,1,4,1,1,1) 33 | % [deriv, headers] = derivnum( 'T', 500, 8,5,3,35,0,25,4200,0,15,1,0,0,1,4,1,1,1) 34 | % [deriv] = derivnum('S',2400,2000:10:2400,1,2,35,0,25,4200,0,15,1,0,0,1,4,1,1,1) 35 | % [deriv] = derivnum('K0',2400,2200,1,2,0:1:35,0,25,4200,0,15,1,0,0,1,4,1,1,1) 36 | % [deriv] = derivnum('K1',2400,2200,1,2,35,0,25,0:100:4200,0,15,1,0,0,1,4,1,1,1) 37 | % 38 | %************************************************************************** 39 | % 40 | % INPUT: 41 | % 42 | % - VARID : character string to select the input variable for which 43 | % derivatives will be taken with respect to. 44 | % This variable appears in denominator of each resulting derivative. 45 | % = variable length, case insensitive, character code 46 | % case 'par1' : Parameter 1 of the input pair (This is TAlk if PAR1TYPE is 1) 47 | % case 'par2' : Parameter 2 of the input pair (This is TAlk if PAR2TYPE is 1) 48 | % case 'sil', 'silt', 'tsil', 'silicate', or 'sit' : Silicate concentration 49 | % case 'phos', 'phost', 'tphos', 'phosphate', or 'pt' : Phosphate concentration 50 | % case 'amm', 'nh4', 'NH4' : Ammonia concentration 51 | % case 'hyd', 'h2s', 'H2S' : Hydrogen Sulfide concentration 52 | % case 't', 'temp' or 'temperature' : temperature 53 | % case 's', 'sal', or 'salinity' : salinity 54 | % case 'K0','K1','K2','Kb','Kw','Kspa', 'Kspc': dissociation constants 55 | % case 'bor': total boron 56 | % 57 | % - all others : same list of input parameters as in CO2SYS() subroutine (version 3, scalar or vectors) 58 | % 59 | %**************************************************************************% 60 | % 61 | % OUTPUT: 3 arrays 62 | % a) an array containing the derivative of the following parameter (one row per sample): 63 | % b) the corresponding cell-array containing crudely formatted headers 64 | % c) the corresponding cell-array containing the units 65 | % 66 | % POS PARAMETER UNIT 67 | % 68 | % 01 - TAlk (umol/kgSW) 69 | % 02 - TCO2 (umol/kgSW) 70 | % 03 - [H+] in (umol/kgSW) 71 | % 04 - pCO2 in (uatm) 72 | % 05 - fCO2 in (uatm) 73 | % 06 - HCO3 in (umol/kgSW) 74 | % 07 - CO3 in (umol/kgSW) 75 | % 08 - CO2 in (umol/kgSW) 76 | % 09 - RF in () 77 | % 10 - OmegaCa in () 78 | % 11 - OmegaAr in () 79 | % 12 - xCO2 in (ppm) 80 | % 13 - [H+] out () 81 | % 14 - pCO2 out (uatm) 82 | % 15 - fCO2 out (uatm) 83 | % 16 - HCO3 out (umol/kgSW) 84 | % 17 - CO3 out (umol/kgSW) 85 | % 18 - CO2 out (umol/kgSW) 86 | % 19 - RF out () 87 | % 20 - OmegaCa out () 88 | % 21 - OmegaAr out () 89 | % 22 - xCO2 out (ppm) 90 | % 91 | % * 'in' refers to INPUT conditions (TEMPIN, PRESIN) as for CO2SYS 92 | % 'out' refers to OUTPUT conditions (TEMPOUT, PRESOUT) 93 | % 94 | % 95 | % CAUTION: derivnum.m is NOT necessarily designed to take partial derivatives 96 | % of input variables, only computed variables relative to input 97 | % variables. However, those partial derivatives of input variables 98 | % are kept in the OUT conditions to maintain consistency 99 | % of the order of output between the different input pairs. 100 | % Generally, we advise not to use these derivatives of input 101 | % variables. However, in some cases their results appear accurate. 102 | % Use them at your own risk. 103 | % 104 | 105 | function [derivatives, headers, units, headers_err, units_err] = ... 106 | derivnum (VARID,PAR1,PAR2,PAR1TYPE,PAR2TYPE, SAL,TEMPIN, ... 107 | TEMPOUT,PRESIN,PRESOUT,SI,PO4,NH4,H2S, ... 108 | pHSCALEIN,K1K2CONSTANTS,KSO4CONSTANT, ... 109 | KFCONSTANT,BORON) 110 | 111 | % For computing derivative with respect to Ks, one has to call CO2sys with a perturbed K 112 | % Requested perturbation is passed through the following global variables 113 | global PertK % Id of perturbed K 114 | global Perturb % perturbation 115 | 116 | % Input conditioning 117 | % ------------------ 118 | 119 | VARID = upper(VARID); 120 | % Determine lengths of input vectors 121 | veclengths=[length(PAR1) length(PAR2) length(PAR1TYPE)... 122 | length(PAR2TYPE) length(SAL) length(TEMPIN)... 123 | length(TEMPOUT) length(PRESIN) length(PRESOUT)... 124 | length(SI) length(PO4) length(NH4) length(H2S)... 125 | length(pHSCALEIN) length(K1K2CONSTANTS)... 126 | length(KSO4CONSTANT) length(KFCONSTANT)... 127 | length(BORON)]; 128 | 129 | if length(unique(veclengths))>2 130 | disp(' '); disp('*** INPUT ERROR: Input vectors must all be of same length, or of length 1. ***'); disp(' '); return 131 | end 132 | 133 | % Make column vectors of all input vectors 134 | PAR1 =PAR1 (:); 135 | PAR2 =PAR2 (:); 136 | PAR1TYPE =PAR1TYPE (:); 137 | PAR2TYPE =PAR2TYPE (:); 138 | SAL =SAL (:); 139 | TEMPIN =TEMPIN (:); 140 | TEMPOUT =TEMPOUT (:); 141 | PRESIN =PRESIN (:); 142 | PRESOUT =PRESOUT (:); 143 | SI =SI (:); 144 | PO4 =PO4 (:); 145 | NH4 =NH4 (:); 146 | H2S =H2S (:); 147 | pHSCALEIN =pHSCALEIN (:); 148 | K1K2CONSTANTS=K1K2CONSTANTS(:); 149 | KSO4CONSTANT =KSO4CONSTANT (:); 150 | KFCONSTANT =KFCONSTANT (:); 151 | BORON =BORON (:); 152 | 153 | % Find the longest column vector: 154 | ntps = max(veclengths); 155 | 156 | % Populate column vectors 157 | PAR1(1:ntps,1) = PAR1(:) ; 158 | PAR2(1:ntps,1) = PAR2(:) ; 159 | PAR1TYPE(1:ntps,1) = PAR1TYPE(:) ; 160 | PAR2TYPE(1:ntps,1) = PAR2TYPE(:) ; 161 | SAL(1:ntps,1) = SAL(:) ; 162 | TEMPIN(1:ntps,1) = TEMPIN(:) ; 163 | TEMPOUT(1:ntps,1) = TEMPOUT(:) ; 164 | PRESIN(1:ntps,1) = PRESIN(:) ; 165 | PRESOUT(1:ntps,1) = PRESOUT(:) ; 166 | SI(1:ntps,1) = SI(:) ; 167 | PO4(1:ntps,1) = PO4(:) ; 168 | SI(1:ntps,1) = SI(:) ; 169 | PO4(1:ntps,1) = PO4(:) ; 170 | NH4(1:ntps,1) = NH4(:) ; 171 | H2S(1:ntps,1) = H2S(:) ; 172 | pHSCALEIN(1:ntps,1) = pHSCALEIN(:) ; 173 | K1K2CONSTANTS(1:ntps,1) = K1K2CONSTANTS(:) ; 174 | KSO4CONSTANT(1:ntps,1) = KSO4CONSTANT(:) ; 175 | KFCONSTANT(1:ntps,1) = KFCONSTANT(:) ; 176 | BORON(1:ntps,1) = BORON(:) ; 177 | 178 | % BASELINE: 179 | % -------- 180 | 181 | carb = CO2SYS(PAR1,PAR2,PAR1TYPE,PAR2TYPE,SAL,TEMPIN,TEMPOUT,PRESIN,PRESOUT,SI,PO4,NH4,H2S,pHSCALEIN,K1K2CONSTANTS,KSO4CONSTANT,KFCONSTANT,BORON); 182 | % Compute [H+] in µmol/KgSW 183 | if (ndims(carb) == 2) 184 | Hin = 10.^(-carb(:,3)) * 1.e6; 185 | Hout = 10.^(-carb(:,21)) * 1.e6; 186 | carb = horzcat(carb(:,1:2), Hin, carb(:,3:20), Hout, carb(:,21:end)); 187 | else 188 | Hin = 10.^(-carb(3)) * 1.e6; 189 | Hout = 10.^(-carb(21)) * 1.e6; 190 | carb = horzcat(carb(1:2), Hin, carb(3:20), Hout, carb(21:end)); 191 | end 192 | 193 | % Compute two slightly different values for input 194 | % ----------------------------------------------- 195 | 196 | % Default values : not perturbed 197 | % no change in PAR1 and PAR2 198 | PAR11 = PAR1; PAR12 = PAR1; 199 | PAR21 = PAR2; PAR22 = PAR2; 200 | % no change in Sil total and Phos total (except for d/dSi, d/dPO4) 201 | SI1 = SI; SI2 = SI; 202 | PO41 = PO4; PO42 = PO4; 203 | % no change in Amm total and H2S total (except for d/dAmm, d/dH2S) 204 | NH41 = NH4; NH42 = NH4; 205 | H2S1 = H2S; H2S2 = H2S; 206 | % no change in T and S (except for d/dS and d/dT, respectively) 207 | TEMP1 = TEMPIN; TEMP2 = TEMPIN; 208 | SAL1 = SAL; SAL2 = SAL; 209 | % no change in TEMPOUT except for d/dT (see why further below) 210 | TEMPOUT1 = TEMPOUT; TEMPOUT2 = TEMPOUT; 211 | 212 | % Create empty vector for abs_dx (absolute delta) 213 | abs_dx = nan(ntps,1); 214 | 215 | % Flag for dissociation constant as perturbed variable 216 | flag_dissoc_K = 0; % False 217 | % names of 7 dissociation constants and variable for total boron 218 | K_names = {'K0', 'K1', 'K2', 'KB', 'KW', 'KSPA', 'KSPC', 'BOR', 'CAL'}; 219 | 220 | % Units for derivatives and for errors 221 | units_at = {'umol';'umol';'nmol';'total scale';'uatm kg';'uatm kg';'umol';'umol';... 222 | 'umol';'kg';'kg';'kg';'ppm kg';... 223 | 'nmol';'uatm kg';'uatm kg';'umol';'umol';... 224 | 'umol';'kg';'kg';'kg';'ppm kg'; 225 | }; 226 | 227 | units_kg = {'umol/kg';'umol/kg';'nmol/kg';'total scale';'uatm';'uatm';'umol/kg';'umol/kg';... 228 | 'umol/kg';' ';' ';' ';'ppm';... 229 | 'nmol/kg';'uatm';'uatm';'umol/kg';'umol/kg';... 230 | 'umol/kg';' ';' ';' ';'ppm'; 231 | }; 232 | 233 | units_k = units_kg; 234 | 235 | units_pco2 = units_kg; 236 | 237 | units_err = units_kg; 238 | 239 | switch VARID 240 | case K_names 241 | flag_dissoc_K = 1; 242 | % Approximate values for K0, K1, K2, Kb, Kspa, Kspc, and CAL 243 | % They will be used to compute an absolute perturbation 244 | % value on these constants 245 | K = [0.034, 1.2e-06, 8.3e-10, 2.1e-09, 6.1e-14, 6.7e-07, ... 246 | 4.3e-07, 0.0004157, 0.0103]; 247 | % Choose value of absolute perturbation 248 | [is_in_K_names, index] = ismember(VARID, K_names); 249 | perturbation = K(index) * 1.e-3; % 0.1 percent of Kx value 250 | abs_dx = 2 * perturbation; 251 | denom_headers = VARID; 252 | denom_units = ' '; 253 | units = units_k; 254 | 255 | case {'PAR1', 'VAR1'} % PAR1 (first variable of input pair) is perturbed 256 | % Define a relative delta 257 | delta = 1.e-6; 258 | 259 | PAR1ref = nan(size(PAR1TYPE)); 260 | units = units_kg; 261 | denom_headers = ''; 262 | denom_units = ''; 263 | t1=PAR1TYPE==1; 264 | PAR1ref(t1) = 2300.; % umol/kg (global surface average, Orr et al., 2017) 265 | t2=PAR1TYPE==2; 266 | PAR1ref(t2) = 2000.; % umol/kg (global surface average, Orr et al., 2017) 267 | t3=PAR1TYPE==3; 268 | PAR1ref(t3) = 1.0e-8; % mol/kg (equivalent to pH=8.0) 269 | t4=PAR1TYPE==4; 270 | PAR1ref(t4) = 400.; % uatm 271 | t5=PAR1TYPE==5; 272 | PAR1ref(t5) = 400.; % uatm 273 | t6=PAR1TYPE==6; 274 | PAR1ref(t6) = 1790.; % umol/kg 275 | t7=PAR1TYPE==7; 276 | PAR1ref(t7) = 200.; % umol/kg 277 | t8=PAR1TYPE==8; 278 | PAR1ref(t8) = 10.; % umol/kg 279 | 280 | % cases where first input variable is pH 281 | F = (PAR1TYPE == 3); 282 | H = 10.^(-PAR1(F)) ; % [H+] in mol/kg 283 | % Change slightly [H+] 284 | H1 = H - PAR1ref(F)*delta; 285 | H2 = H + PAR1ref(F)*delta; 286 | PAR11(F) = -log10(H1) ; 287 | PAR12(F) = -log10(H2) ; 288 | abs_dx(F) = (H2 - H1) * 1e9; % now in nmol/kg 289 | 290 | G = ~F; 291 | % Change slightly PAR1 292 | PAR11(G) = PAR1(G) - PAR1ref(G)*delta; 293 | PAR12(G) = PAR1(G) + PAR1ref(G)*delta; 294 | abs_dx(G) = PAR12(G) - PAR11(G); 295 | 296 | case {'PAR2', 'VAR2'} % PAR2 (second variable of input pair) is perturbed 297 | % Define a relative delta 298 | delta = 1.e-6; 299 | 300 | PAR2ref = nan(size(PAR2TYPE)); 301 | units = units_kg; 302 | denom_headers = ''; 303 | denom_units = ''; 304 | t1=PAR2TYPE==1; 305 | PAR2ref(t1) = 2300.; % umol/kg (global surface average, Orr et al., 2017) 306 | t2=PAR2TYPE==2; 307 | PAR2ref(t2) = 2000.; % umol/kg (global surface average, Orr et al., 2017) 308 | t3=PAR2TYPE==3; 309 | PAR2ref(t3) = 1.0e-8; % mol/kg (equivalent to pH=8.0) 310 | t4=PAR2TYPE==4; 311 | PAR2ref(t4) = 400.; % uatm 312 | t5=PAR2TYPE==5; 313 | PAR2ref(t5) = 400.; % uatm 314 | t6=PAR2TYPE==6; 315 | PAR2ref(t6) = 1790.; % umol/kg 316 | t7=PAR2TYPE==7; 317 | PAR2ref(t7) = 200.; % umol/kg 318 | t8=PAR2TYPE==8; 319 | PAR2ref(t8) = 10.; % umol/kg 320 | 321 | 322 | % cases where second input variable is pH 323 | F = (PAR2TYPE == 3); 324 | H = 10.^(-PAR2(F)) ; % H+ in mol/kg 325 | % Change slightly [H+] 326 | H1 = H - PAR2ref(F)*delta; 327 | H2 = H + PAR2ref(F)*delta; 328 | PAR21(F) = -log10(H1) ; 329 | PAR22(F) = -log10(H2) ; 330 | abs_dx(F) = (H2 - H1) * 1e9; 331 | 332 | G = ~F; 333 | % Change slightly PAR2 334 | PAR21(G) = PAR2(G) - PAR2ref(G)*delta; 335 | PAR22(G) = PAR2(G) + PAR2ref(G)*delta; 336 | abs_dx(G) = PAR22(G) - PAR21(G); 337 | 338 | case {'SIL', 'TSIL', 'SILT', 'SILICATE', 'SIT'} % Sil total 339 | % Define a relative delta 340 | delta = 1.e-3; 341 | SIref = 7.5; % umol/kg (global surface average, Orr et al., 2018) 342 | % Change slightly SI 343 | SI1 = SI - SIref*delta; 344 | SI2 = SI + SIref*delta; 345 | abs_dx = SI2 - SI1; 346 | denom_headers = 'Sit'; 347 | denom_units = 'umol'; 348 | units = units_at; 349 | case {'PHOS', 'TPHOS', 'PHOST', 'PHOSPHATE', 'PT'} % Phos total 350 | % Define a relative delta 351 | delta = 1.e-3; 352 | PO4ref = 0.5; % umol/kg (global surface average, Orr et al., 2018) 353 | % Change slightly PO4 354 | PO41 = PO4 - PO4ref*delta; 355 | PO42 = PO4 + PO4ref*delta; 356 | abs_dx = PO42 - PO41; 357 | denom_headers = 'Pt'; 358 | denom_units = 'umol'; 359 | units = units_at; 360 | case {'AMM','NH4'} % Amm total 361 | % Define a relative delta 362 | delta = 1.e-3; 363 | NH4ref = 1; 364 | % Change slightly NH4 365 | NH41 = NH4 - NH4ref*delta; 366 | NH42 = NH4 + NH4ref*delta; 367 | abs_dx = NH42 - NH41; 368 | denom_headers = 'NH4t'; 369 | denom_units = 'umol'; 370 | units = units_at; 371 | case {'HYD','H2S'} % H2S total 372 | % Define a relative delta 373 | delta = 1.e-3; 374 | H2Sref = 1; 375 | % Change slightly H2S 376 | H2S1 = H2S - H2Sref*delta; 377 | H2S2 = H2S + H2Sref*delta; 378 | abs_dx = H2S2 - H2S1; 379 | denom_headers = 'H2St'; 380 | denom_units = 'umol'; 381 | units = units_at; 382 | case {'T', 'TEMP', 'TEMPERATURE'} % Temperature 383 | % Define a relative delta 384 | delta = 1.e-4; 385 | TEMPref = 18.; % global surface mean (C) 386 | % Change slightly temperature 387 | TEMP1 = TEMPIN - TEMPref*delta; 388 | TEMP2 = TEMPIN + TEMPref*delta; 389 | abs_dx = TEMP2 - TEMP1; 390 | % When computing d/dT 391 | % TEMPOUT changes must be the same as TEMPIN changes, e.g, for 392 | % derivnum 'OUT' & 'IN' results to be the same when TEMPOUT=TEMPIN 393 | TEMPOUT1 = TEMPOUT - TEMPref*delta; 394 | TEMPOUT2 = TEMPOUT + TEMPref*delta; 395 | denom_headers = 'T'; 396 | denom_units = 'C'; 397 | units = units_kg; 398 | case {'S', 'SAL', 'SALINITY'} % Salinity 399 | % Define a relative delta 400 | delta = 1.e-4; 401 | SALref = 35.; 402 | % Change slightly salinity 403 | SAL1 = SAL - SALref*delta; 404 | SAL2 = SAL + SALref*delta; 405 | abs_dx = SAL2 - SAL1; 406 | denom_headers = 'S'; 407 | denom_units = 'psu'; 408 | units = units_kg; 409 | end 410 | 411 | % PERTURBATION: 412 | % ------------- 413 | 414 | % Point 1: (one dissociation constant or PAR1, PAR2, T or S is somewhat smaller) 415 | % if perturbed variable is a dissociation constant 416 | if (flag_dissoc_K) 417 | PertK = upper(VARID); 418 | Perturb = -perturbation; 419 | end 420 | cdel1 = CO2SYS ( ... 421 | PAR11,PAR21,PAR1TYPE,PAR2TYPE,SAL1,TEMP1,TEMPOUT1,PRESIN,PRESOUT,SI1,PO41,NH41,H2S1,pHSCALEIN,K1K2CONSTANTS,KSO4CONSTANT,KFCONSTANT,BORON); 422 | % Compute [H+] 423 | if (ndims(cdel1) == 2) 424 | Hin = 10.^(-cdel1(:,3)) * 1.e9; % to show H+ results in nmol/kg 425 | Hout = 10.^(-cdel1(:,21)) * 1.e9; 426 | cdel1 = horzcat(cdel1(:,1:2), Hin, cdel1(:,3:20), Hout, cdel1(:,21:end)); 427 | else 428 | Hin = 10.^(-cdel1(3)) * 1.e9; 429 | Hout = 10.^(-cdel1(21)) * 1.e9; 430 | cdel1 = horzcat(cdel1(1:2), Hin, cdel1(3:20), Hout, cdel1(21:end)); 431 | end 432 | 433 | % Point 2: (one dissociation constant or PAR1, PAR2, T or S is somewhat bigger) 434 | % if perturbed variable is a dissociation constant 435 | if (flag_dissoc_K) 436 | PertK = upper(VARID); 437 | Perturb = perturbation; 438 | end 439 | cdel2 = CO2SYS ( ... 440 | PAR12,PAR22,PAR1TYPE,PAR2TYPE,SAL2,TEMP2,TEMPOUT2,PRESIN,PRESOUT,SI2,PO42,NH42,H2S2,pHSCALEIN,K1K2CONSTANTS,KSO4CONSTANT,KFCONSTANT,BORON); 441 | % Compute [H+] 442 | if (ndims(cdel2) == 2) 443 | % Computed variable H+ (does not affect other computed 444 | % variables, i.e., it is the numerator of the derivative) 445 | Hin = 10.^(-cdel2(:,3)) * 1.e9; % to show H+ results in nmol/kg 446 | Hout = 10.^(-cdel2(:,21)) * 1.e9; 447 | cdel2 = horzcat(cdel2(:,1:2), Hin, cdel2(:,3:20), Hout, cdel2(:,21:end)); 448 | else 449 | Hin = 10.^(-cdel2(3)) * 1.e9; 450 | Hout = 10.^(-cdel2(21)) * 1.e9; 451 | cdel2 = horzcat(cdel2(1:2), Hin, cdel2(3:20), Hout, cdel2(21:end)); 452 | end 453 | 454 | % if perturbed variable is a dissociation constant 455 | if (flag_dissoc_K) 456 | PertK = ''; %% Return to normal 457 | end 458 | 459 | % Drop unnecessary columns 460 | % Note: columns (04 - pHin) and (20 - pHout) drop 461 | % Keep only the following columns 462 | % 01 - TAlk (umol/kgSW) 463 | % 02 - TCO2 (umol/kgSW) 464 | % 03 - [H+] in (nmol/kgSW) lastly added 465 | % 05 - pCO2 in (uatm) 466 | % 06 - fCO2 in (uatm) 467 | % 07 - HCO3 in (umol/kgSW) 468 | % 08 - CO3 in (umol/kgSW) 469 | % 09 - CO2 in (umol/kgSW) 470 | % 17 - RF in () 471 | % 18 - OmegaCa in () 472 | % 19 - OmegaAr in () 473 | % 20 - xCO2 in (ppm) 474 | % 22 - [H+] out (nmol/kgSW) lastly added 475 | % 24 - pCO2 out (uatm) 476 | % 25 - fCO2 out (uatm) 477 | % 26 - HCO3 out (umol/kgSW) 478 | % 27 - CO3 out (umol/kgSW) 479 | % 28 - CO2 out (umol/kgSW) 480 | % 36 - RF out () 481 | % 37 - OmegaCa out () 482 | % 38 - OmegaAr out () 483 | % 39 - xCO2 out (ppm) 484 | keep = [1 2 3 5 6 7 8 9 17 18 19 20 22 24 25 26 27 28 36 37 38 39]; 485 | 486 | % We will drop also some column headers 487 | headers = {'TAlk';'TCO2';'Hin';'pHin';'pCO2in';'fCO2in';'HCO3in';'CO3in';... 488 | 'CO2in';'RFin';'OmegaCAin';'OmegaARin';'xCO2in';... 489 | 'Hout';'pCO2out';'fCO2out';'HCO3out';'CO3out';... 490 | 'CO2out';'RFout';'OmegaCAout';'OmegaARout';'xCO2out'}; 491 | headers_err = headers; 492 | %units = {'umol';'umol';'nmol';'total scale';'uatm';'uatm';'umol';'umol';... 493 | % 'umol';' ';' ';'ppm';... 494 | % 'nmol';'uatm';'uatm';'umol';'umol';... 495 | % 'umol';' ';' ';'ppm'; 496 | % }; 497 | % Initially, keep all headers except 'pHin' 498 | keep_head = [1:3 5:23]; 499 | 500 | % **** This was previously uncommented to eliminate partial derivatives 501 | % **** of input variables 502 | % 503 | % % if all parameter PAR1 are of the same type 504 | % if all(PAR1TYPE == PAR1TYPE(1)) 505 | % % Determine column number of PAR1 506 | % if PAR1TYPE(1) <= 3 % TAlk, TCO2 or pH 507 | % % By design of CO2sys, PARTYPE is equal to column number 508 | % col_number = PAR1TYPE(1); 509 | % else 510 | % % Because there is an extra column: [H+] 511 | % col_number = PAR1TYPE(1) + 1; 512 | % end 513 | % % Exclude input parameters PAR1 514 | % A = (keep ~= col_number); 515 | % keep = keep (A); 516 | % A = (keep_head ~= col_number); 517 | % keep_head = keep_head (A); 518 | % end 519 | % % if all parameter PAR2 are of the same type 520 | % if all(PAR2TYPE == PAR2TYPE(1)) 521 | % % Determine column number of PAR1 522 | % if PAR2TYPE(1) <= 3 % TAlk, TCO2 or pH 523 | % % By design of CO2sys, PARTYPE is equal to column number 524 | % col_number = PAR2TYPE(1); 525 | % else 526 | % % Because there is an extra column: [H+] 527 | % col_number = PAR2TYPE(1) + 1; 528 | % end 529 | % % Exclude input parameters PAR2 530 | % A = (keep ~= col_number); 531 | % keep = keep (A); 532 | % A = (keep_head ~= col_number); 533 | % keep_head = keep_head (A); 534 | % end 535 | 536 | cdel1 = cdel1(:,keep); 537 | cdel2 = cdel2(:,keep); 538 | 539 | headers = headers(keep_head); 540 | units = units(keep_head); 541 | 542 | headers_err = headers_err(keep_head); 543 | units_err = units_err(keep_head); 544 | 545 | % concatenate strings to give each header its proper form, a partial derivative 546 | headers = strcat('d', headers, '/', 'd', denom_headers); 547 | % concatenate in an analogous way for the units 548 | units = strcat('(',units, '/', denom_units,')'); 549 | 550 | % Centered difference 551 | dy = (cdel2 - cdel1); 552 | 553 | % Compute ratio dy/dx 554 | if (isscalar(abs_dx)) 555 | derivatives = dy ./ abs_dx; 556 | else 557 | derivatives = bsxfun(@rdivide, dy, abs_dx); 558 | end 559 | 560 | % **** This was previously uncommented to mask derivatives of 561 | % **** parameters that are directly related to input variables 562 | % 563 | % % Mask values that should not be used with NaN (e.g., dHout/dT when PAR1 or PAR2 is pH) 564 | % switch VARID 565 | % case {'T', 'TEMP', 'TEMPERATURE'} 566 | % % For PAR1TYPE or PAR2TYPE = 3 (pH is input) make dHout/dT value a NaN 567 | % F = (PAR1TYPE==3 | PAR2TYPE==3); % either CO2 system input variable is pH 568 | % [is_in_headers, idx] = ismember('dHout/dT', headers); 569 | % if any(is_in_headers) 570 | % derivatives(F,idx) = NaN ; 571 | % end 572 | % 573 | % % For PAR1TYPE or PAR2TYPE = 4 or 5 (pCO2 or fCO2 is input) make relevant d/dT values NaNs 574 | % F = (PAR1TYPE==4 | PAR2TYPE==4); %when pCO2 or fCO2 is input var 575 | % % masknan = {'dfCO2in/dT' 'dxCO2in/dT' 'dpCO2out/dT' 'dfCO2out/dT' 'dxCO2out/dT'}; 576 | % masknan = {'dpCO2out/dT'}; 577 | % [is_in_headers, idx] = ismember(masknan, headers); 578 | % if any(is_in_headers) 579 | % derivatives(F,idx) = NaN ; 580 | % end 581 | % 582 | % % For PAR1TYPE or PAR2TYPE = 5 (fCO2 is input) make relevant d/dT values NaNs 583 | % F = (PAR1TYPE==5 | PAR2TYPE==5); %when fCO2 is input var 584 | % % masknan = {'dfCO2in/dT' 'dxCO2in/dT' 'dpCO2out/dT' 'dfCO2out/dT' 'dxCO2out/dT'}; 585 | % masknan = {'dfCO2out/dT'}; 586 | % [is_in_headers, idx] = ismember(masknan, headers); 587 | % if any(is_in_headers) 588 | % derivatives(F,idx) = NaN ; 589 | % end 590 | % 591 | % case {'PAR1', 'VAR1'} 592 | % % For pH-pCO2 or pH-fCO2 pair (PAR1 is pH) 593 | % F = (PAR1TYPE==3 & (PAR2TYPE==4 | PAR2TYPE==5)); 594 | % masknan = {'dHout/dH' 'dpCO2out/dH' 'dfCO2out/dH'}; 595 | % [is_in_headers, idx] = ismember(masknan, headers); 596 | % if any(is_in_headers) 597 | % derivatives(F,idx) = NaN ; 598 | % end 599 | % 600 | % case {'PAR2', 'VAR2'} 601 | % % For pCO2-pH or fCO2-pH pair (PAR2 is pH) 602 | % F = ((PAR1TYPE==4 | PAR1TYPE==5) & PAR2TYPE==3); 603 | % masknan = {'dHout/dH' 'dpCO2out/dH' 'dfCO2out/dH'}; 604 | % [is_in_headers, idx] = ismember(masknan, headers); 605 | % if any(is_in_headers) 606 | % derivatives(F,idx) = NaN ; 607 | % end 608 | % end 609 | 610 | end 611 | -------------------------------------------------------------------------------- /main/errors.m: -------------------------------------------------------------------------------- 1 | % errors() 2 | % This subroutine propagates uncertainties for the marine carbonate chemistry calculations 3 | % from errors (or uncertainties) on inputs: 4 | % - pair of carbonate system variables 5 | % - nutrient (silicate and phosphate) concentrations 6 | % - concentrations of ammonium and hydrogen sulfide 7 | % - temperature and salinity 8 | % - calcium concentration (optional) 9 | % plus errors in dissociation constants pK0, pK1, pK2, pKb, pKw, pKspa, and pKspc as well as total boron 10 | % 11 | % It calls derivnum, which computes numerical derivatives, and then 12 | % it applies error propagation using the method of moments. 13 | % The latter is a general technique to estimate the 2nd moment of a variable z 14 | % (variance or standard deviation) based on a 1st-order approximation to z. 15 | % 16 | % This subroutine has been modified from its original version (Orr et al. 17 | % 2018) to allow for input variables of CO2, HCO3, and CO3, the inclusion 18 | % of ammonium and hydrogen sulfide, compatibility with CO2SYS.m(v3), and am 19 | % optional input of calcium concentration uncertainty. 20 | % 21 | %************************************************************************** 22 | % 23 | % **** SYNTAX: 24 | % [err, headers, units] = errors(PAR1,PAR2,PAR1TYPE,PAR2TYPE,... 25 | % SAL,TEMPIN,TEMPOUT,PRESIN,PRESOUT,SI,PO4,... 26 | % NH4,H2S,ePAR1,ePAR2,eSAL,eTEMP,eSI,ePO4,... 27 | % eNH4,eH2S,epK,eBt,r,pHSCALEIN,K1K2CONSTANTS,... 28 | % KSO4CONSTANT,KFCONSTANT,BORON,eCAL(optional)) 29 | % 30 | % **** SYNTAX EXAMPLES: 31 | % [Result] = errors(2400,2200,1,2,35,10,10,0,0,15,1,0,0,2,2,0.01,0.01,0,0,0,0,0,0,0,1,4,1,1,1) 32 | % [Result,Headers] = errors(2400, 8,1,3,35,25,5,0,3000,15,1,0,0,2,0.001,0,0,0,0,0,0,0,0,0,1,4,1,1,1) 33 | % [Result,Headers,Units] = errors(500, 8,5,3,35,25,5,0,4000,15,1,0,0,2,0.001,0,0,0,0,0,0,'','',0,1,4,1,1,1) 34 | % [A] = errors(2400,2000:10:2400,1,2,35,10,10,0,0,15,2,0,0,2,0,0,0,0,0,0,0,'','',0,1,4,1,1,1) 35 | % [A] = errors(2400,2200,1,2,0:1:35,0,25,4200,0,15,1,0,0,2,2,0,0,0,0,0,0,'','',0,1,4,1,1,1) 36 | % epK = [0.002, 0.0075, 0.015, 0.01, 0.01, 0.02, 0.02]; 37 | % eBt = 0.02; 38 | % [A, hdr, units] = errors(2400,2200,1,2,35,0,25,0:100:4200,0,15,1,0,0,2,2,0,0,0,0,0,0,epK,eBt,0,1,4,1,1,1) 39 | % 40 | %************************************************************************** 41 | % 42 | % INPUT: 43 | % 44 | % - ePAR1, ePAR2 : uncertainty of PAR1 and PAR2 of input pair of CO2 system variables 45 | % * Same units as PAR1 & PAR2 46 | % - eS, eT : uncertainty of Salinity and Temperature (same units as S and T) 47 | % - ePO4, eSI : uncertainty of Phosphate and Silicate total concentrations (same units as PO4 and SI [umol/kg]) 48 | % - eNH4, eH2S : uncertainty of Ammonia and Hydrogen Sulfide total concentrations (same units as NH4 and H2S [umol/kg]) 49 | % - epK : uncertainty of all seven dissociation constants (a vector) [pK units] 50 | % - eBt : uncertainty of total boron, given as fractional relative error (eBt=0.02 is a 2% error) 51 | % - r : correlation coefficient between PAR1 AND PAR2 (typicaly 0) 52 | % - others : same as input for subroutine CO2SYS() (version 3, scalar or vectors) 53 | % - eCAL : uncertainty of calcium [mmol/kg] determined by ratio with salinity 54 | % 55 | % All parameters may be scalars or vectors except epK and eBt. 56 | % * epK must be vector of 7 values : errors of [pK0, pK1, pK2, pKb, pKw, pKspa, pKspc]. 57 | % These errors are assumed to be the same for all rows of data. 58 | % These 7 values are in pK units 59 | % 60 | % if epK is empty (= ''), this routine specifies default values. 61 | % These default standard errors are : 62 | % pK0 : 0.002 63 | % pK1 : 0.0075 64 | % pK2 : 0.015 65 | % pKb : 0.01 boric acid 66 | % pKw : 0.01 water dissociation 67 | % pKspa : 0.02 solubility product of Aragonite 68 | % pKspc : 0.02 solubility product of Calcite 69 | % 70 | % * eBt is a scalar real number, fractional relative error (between 0.00 and 1.00) 71 | % for TB, where the default is eBt=0.02. It is assumed to be the same 72 | % for all rows of data. 73 | % 74 | % In constrast, ePAR1, ePAR2, eS, eT, ePO4, eSI, eNH4, and eH2S 75 | % - if vectors, are errors associated with each data point 76 | % - if scalars, are one error value associated to all data points 77 | % The same for parameter 'r'. 78 | % 79 | % If no value is input for eCAL, it will not be evaluated. 80 | % 81 | % If 'r' is nonzero with a value between -1.0 and 1.0, it indicates the correlation 82 | % between uncertainties of the input pair of carbonate system variables. 83 | % By default, 'r' is zero. However, for some pairs the user may want to specify a 84 | % different value. For example, measurements of pCO2 and pH are often anti-correlated. 85 | % The same goes for two other pairs: 'CO2 and CO3' and 'pCO2 and 86 | % CO3'. But even for these cases, care is needed when using non-zero values of 'r'. 87 | % 88 | % When the user propagates errors for an individual 89 | % measurement, 'r' should ALWAYS be zero if each member of the input pair is 90 | % measured independently. In this case, we are interested in the 91 | % correlation between the uncertainties in those measurements, not in 92 | % the correlation between the measurments themselves. Uncertainties from 93 | % those measurements are probably not correlated if they come from 94 | % different instruments. Conversely, if users are interested in the 95 | % error in the mean of a distribution of measurements (i.e., if they are 96 | % propagating standard errors instead of standard deviations), one 97 | % should then also account for the correlation between the measurements of 98 | % the two variables of the input pair. 99 | % 100 | % For input pairs where one member is pH, this 'errors' routine automatically 101 | % inverses the sign of 'r'. 102 | % That inversion is done because the associated derivatives are computed in terms of 103 | % the hydrogen ion concentration H+, not pH. Therefore for each of these 6 104 | % flags, if the user wants to compute 'r' that should be done (1) using 105 | % the H+ concentration instead of pH, and (2) the sign of that computed 'r' 106 | % should be inversed when passing it as an argument to this routine. 107 | % To express perfect anticorrelation with pH, the user should 108 | % use 'r=-1.0'. 109 | % 110 | %************************************************************************** 111 | % 112 | % OUTPUT: * an array containing uncertainty for the following variables 113 | % (one row per sample): 114 | % * a cell-array containing crudely formatted headers 115 | % 116 | % POS PARAMETER UNIT 117 | % 118 | % 01 - TAlk (umol/kgSW) 119 | % 02 - TCO2 (umol/kgSW) 120 | % 03 - [H+] in (nmol/kgSW) 121 | % 04 - pCO2 in (uatm) 122 | % 05 - fCO2 in (uatm) 123 | % 06 - HCO3 in (umol/kgSW) 124 | % 07 - CO3 in (umol/kgSW) 125 | % 08 - CO2 in (umol/kgSW) 126 | % 09 - RF in () 127 | % 10 - OmegaCa in () 128 | % 11 - OmegaAr in () 129 | % 12 - xCO2 in (ppm) 130 | % 13 - [H+] out (nmol/kgSW) 131 | % 14 - pCO2 out (uatm) 132 | % 15 - fCO2 out (uatm) 133 | % 16 - HCO3 out (umol/kgSW) 134 | % 17 - CO3 out (umol/kgSW) 135 | % 18 - CO2 out (umol/kgSW) 136 | % 19 - RF out () 137 | % 20 - OmegaCa out () 138 | % 21 - OmegaAr out () 139 | % 22 - xCO2 out (ppm) 140 | % 141 | % NOTE: User-specified uncertainties for the input arguments are provided 142 | % in addition to uncertainties in output arguments at both input and 143 | % output conditions. 144 | % 145 | 146 | function [total_error, headers, units] = ... 147 | errors (PAR1, PAR2, PAR1TYPE, PAR2TYPE, SAL, TEMPIN, TEMPOUT, PRESIN, PRESOUT, SI, PO4,... 148 | NH4, H2S, ePAR1, ePAR2, eSAL, eTEMP, eSI, ePO4, eNH4, eH2S, epK, eBt, r, ... 149 | pHSCALEIN,K1K2CONSTANTS,KSO4CONSTANT,KFCONSTANT,BORON,eCAL) 150 | 151 | global K0 K1 K2 KW KB KF KS KP1 KP2 KP3 KSi KNH4 KH2S E; 152 | 153 | % Input conditioning 154 | % ------------------ 155 | 156 | % Handle optional calcium error input 157 | if (nargin < 30), eCAL=0; end 158 | 159 | % Determine lengths of input vectors 160 | veclengths=[length(PAR1) length(PAR2) length(PAR1TYPE)... 161 | length(PAR2TYPE) length(SAL) length(TEMPIN)... 162 | length(TEMPOUT) length(PRESIN) length(PRESOUT)... 163 | length(SI) length(PO4) length(NH4) length(H2S)... 164 | length(ePAR1) length(ePAR2) length(eSAL) length(eTEMP)... 165 | length(eSI) length(ePO4) length(eNH4) length(eH2S)... 166 | length(r) length(pHSCALEIN) length(K1K2CONSTANTS)... 167 | length(KSO4CONSTANT) length(KFCONSTANT) length(BORON)... 168 | length(eCAL)]; 169 | 170 | if length(unique(veclengths))>2 171 | disp(' '); disp('*** INPUT ERROR: Input vectors must all be of same length, or of length 1. ***'); disp(' '); return 172 | end 173 | 174 | % Make column vectors of all input vectors 175 | PAR1 =PAR1 (:); 176 | PAR2 =PAR2 (:); 177 | PAR1TYPE =PAR1TYPE (:); 178 | PAR2TYPE =PAR2TYPE (:); 179 | SAL =SAL (:); 180 | TEMPIN =TEMPIN (:); 181 | TEMPOUT =TEMPOUT (:); 182 | PRESIN =PRESIN (:); 183 | PRESOUT =PRESOUT (:); 184 | SI =SI (:); 185 | PO4 =PO4 (:); 186 | NH4 =NH4 (:); 187 | H2S =H2S (:); 188 | ePAR1 =ePAR1 (:); 189 | ePAR2 =ePAR2 (:); 190 | eSAL =eSAL (:); 191 | eTEMP =eTEMP (:); 192 | eSI =eSI (:); 193 | ePO4 =ePO4 (:); 194 | eNH4 =eNH4 (:); 195 | eH2S =eH2S (:); 196 | r =r (:); 197 | pHSCALEIN =pHSCALEIN (:); 198 | K1K2CONSTANTS=K1K2CONSTANTS(:); 199 | KSO4CONSTANT =KSO4CONSTANT (:); 200 | KFCONSTANT =KFCONSTANT (:); 201 | BORON =BORON (:); 202 | eCAL =eCAL (:); 203 | 204 | % Find the longest column vector: 205 | ntps = max(veclengths); 206 | 207 | % Populate column vectors 208 | PAR1(1:ntps,1) = PAR1(:) ; 209 | PAR2(1:ntps,1) = PAR2(:) ; 210 | PAR1TYPE(1:ntps,1) = PAR1TYPE(:) ; 211 | PAR2TYPE(1:ntps,1) = PAR2TYPE(:) ; 212 | SAL(1:ntps,1) = SAL(:) ; 213 | TEMPIN(1:ntps,1) = TEMPIN(:) ; 214 | TEMPOUT(1:ntps,1) = TEMPOUT(:) ; 215 | PRESIN(1:ntps,1) = PRESIN(:) ; 216 | PRESOUT(1:ntps,1) = PRESOUT(:) ; 217 | SI(1:ntps,1) = SI(:) ; 218 | PO4(1:ntps,1) = PO4(:) ; 219 | NH4(1:ntps,1) = NH4(:) ; 220 | H2S(1:ntps,1) = H2S(:) ; 221 | ePAR1(1:ntps,1) = ePAR1(:) ; 222 | ePAR2(1:ntps,1) = ePAR2(:) ; 223 | eSAL(1:ntps,1) = eSAL(:) ; 224 | eTEMP(1:ntps,1) = eTEMP(:) ; 225 | eSI(1:ntps,1) = eSI(:) ; 226 | ePO4(1:ntps,1) = ePO4(:) ; 227 | eNH4(1:ntps,1) = eNH4(:) ; 228 | eH2S(1:ntps,1) = eH2S(:) ; 229 | r(1:ntps,1) = r(:) ; 230 | pHSCALEIN(1:ntps,1) = pHSCALEIN(:) ; 231 | K1K2CONSTANTS(1:ntps,1) = K1K2CONSTANTS(:) ; 232 | KSO4CONSTANT(1:ntps,1) = KSO4CONSTANT(:) ; 233 | KFCONSTANT(1:ntps,1) = KFCONSTANT(:) ; 234 | BORON(1:ntps,1) = BORON(:) ; 235 | eCAL(1:ntps,1) = eCAL(:) ; 236 | 237 | % Exclude input variables equal to -999 238 | E = (PAR1~=-999 & PAR2~=-999); 239 | 240 | % Default values for epK 241 | if (isempty(epK)) 242 | epK = [0.002, 0.0075, 0.015, 0.01, 0.01, 0.02, 0.02]; 243 | else 244 | % Check validity of epK 245 | if (length(epK) == 1 && epK == 0) 246 | % this means that the caller does not want to account for errors on dissoc. constants 247 | epK = [0.0 0.0 0.0 0.0 0.0 0.0 0.0]; 248 | elseif (length(epK) ~= 7) 249 | error ('invalid parameter epK: ', epK) 250 | end 251 | end 252 | 253 | % Default value for eBt (also check for incorrectly specified values 254 | if (isempty(eBt)) 255 | eBt = 0.02; 256 | elseif ( ~(isscalar(eBt)) ) 257 | error ('invalid parameter eBt (must be scalar): ') 258 | elseif ( isscalar(eBt)) 259 | if (eBt < 0 || eBt > 1) 260 | error ('The "eBt" input argument is the fractional error. It must be between 0 and 1. Default is 0.02 (a 2% error).') 261 | end 262 | end 263 | 264 | % names of dissociation constants 265 | Knames = {'K0','K1','K2','Kb','Kw','Kspa', 'Kspc'}; 266 | 267 | % Convert error on pH to error on [H+] concentration 268 | % in case where first input variable is pH 269 | isH = (E & PAR1TYPE == 3); 270 | 271 | pH = PAR1(isH); 272 | epH = ePAR1(isH); % Error on pH 273 | H = 10.^(-pH); % H+ concentration 274 | r(isH) = -r(isH); % Inverse sign of 'r' if PAR1 is pH 275 | 276 | % dpH = d(-log10[H]) 277 | % = d(- ln[H] / ln[10] ) 278 | % = -(1/ln[10]) * d (ln[H]) 279 | % = -(1/ln[10]) * (dH / H) 280 | % Thus dH = - ln[1O] * [H] dpH 281 | eH = log(10) * (H .* epH); % Removed the minus sign because all errors (sigmas) are positive by definition 282 | eH = eH * 1e9 ; % Convert from mol/kg to nmol/kg (to have same units as partial derivative) 283 | ePAR1(isH) = eH; 284 | 285 | % Same conversion for second variable 286 | isH = (E & PAR2TYPE == 3); 287 | pH = PAR2(isH); 288 | epH = ePAR2(isH); % Error on pH 289 | H = 10.^(-pH); % H+ concentration 290 | r(isH) = -r(isH); % Inverse sign of 'r' if PAR2 is pH 291 | 292 | eH = log(10) * (H .* epH); 293 | eH = eH * 1e9; 294 | ePAR2(isH) = eH; 295 | 296 | % Convert calcium error from mmol/kg to mol/kg 297 | eCAL = eCAL.*1e-3; 298 | 299 | % initialise total square error 300 | sq_err = zeros(ntps,1); 301 | 302 | % Contribution of PAR1 to squared standard error 303 | if (any(ePAR1 ~= 0.0)) 304 | % Compute sensitivities (partial derivatives) 305 | [deriv1(E,:),~,~,headers_err,units_err] = derivnum ('PAR1',... 306 | PAR1(E),PAR2(E),PAR1TYPE(E),PAR2TYPE(E),SAL(E),TEMPIN(E),... 307 | TEMPOUT(E),PRESIN(E),PRESOUT(E),SI(E),PO4(E),NH4(E),H2S(E),... 308 | pHSCALEIN(E),K1K2CONSTANTS(E),KSO4CONSTANT(E),KFCONSTANT(E),BORON(E)); 309 | err = bsxfun(@times,deriv1,ePAR1); 310 | sq_err = bsxfun(@plus,err*0., sq_err); 311 | sq_err = sq_err + err .* err; 312 | end 313 | 314 | % Contribution of PAR2 to squared standard error 315 | if (any (ePAR2 ~= 0.0)) 316 | % Compute sensitivities (partial derivatives) 317 | [deriv2(E,:),~,~,headers_err,units_err] = derivnum ('PAR2',... 318 | PAR1(E),PAR2(E),PAR1TYPE(E),PAR2TYPE(E),SAL(E),TEMPIN(E),... 319 | TEMPOUT(E),PRESIN(E),PRESOUT(E),SI(E),PO4(E),NH4(E),H2S(E),... 320 | pHSCALEIN(E),K1K2CONSTANTS(E),KSO4CONSTANT(E),KFCONSTANT(E),BORON(E)); 321 | err = bsxfun(@times,deriv2,ePAR2); 322 | sq_err = bsxfun(@plus,err*0., sq_err); 323 | sq_err = sq_err + err .* err; 324 | end 325 | 326 | % Contribution of covariance of PAR1 and PAR2 to squared standard error 327 | covariance = nan(ntps,1); 328 | if (any (r ~= 0.0) && any (ePAR1 ~= 0.0) && any (ePAR2 ~= 0.0)) 329 | % Compute covariance from correlation coeff. & std deviations 330 | covariance(E) = r(E) .* ePAR1(E) .* ePAR2(E); 331 | % Contribution to squared error 332 | err2 = bsxfun(@times,2 * deriv1 .* deriv2, covariance); 333 | sq_err = sq_err + err2; 334 | end 335 | 336 | % Contribution of Silion (total dissolved inorganic concentration) to squared standard error 337 | % 338 | % Remark : does not compute error where SI = 0 339 | % because computation of sensitivity to SI fails in that case 340 | % 341 | SI_valid = (E & SI ~= 0 & eSI ~= 0); 342 | if (any (SI_valid)) 343 | % Compute sensitivities (partial derivatives) 344 | [deriv, ~, ~, headers_err, units_err] = derivnum ('sil',PAR1(SI_valid),PAR2(SI_valid),PAR1TYPE(SI_valid),PAR2TYPE(SI_valid),... 345 | SAL(SI_valid),TEMPIN(SI_valid),TEMPOUT(SI_valid),PRESIN(SI_valid),PRESOUT(SI_valid),... 346 | SI(SI_valid),PO4(SI_valid),NH4(SI_valid),H2S(SI_valid),pHSCALEIN(SI_valid),K1K2CONSTANTS(SI_valid),KSO4CONSTANT(SI_valid),... 347 | KFCONSTANT(SI_valid),BORON(SI_valid)); 348 | err = bsxfun(@times,deriv,eSI(SI_valid)); 349 | new_size = [ntps size(err,2)]; 350 | sq_err = zeros(new_size) + sq_err; 351 | sq_err(SI_valid,:) = sq_err(SI_valid,:) + err .* err; 352 | end 353 | 354 | % Contribution of Phosphorus (total dissoloved inorganic concentration) to squared standard error 355 | % 356 | % Remark : does not compute error where PO4 = 0 357 | % because computation of sensitivity to PO4 fails in that case 358 | % 359 | PO4_valid = (E & PO4 ~= 0 & ePO4 ~= 0); 360 | if (any (PO4_valid)) 361 | % Compute sensitivities (partial derivatives) 362 | [deriv, ~, ~, headers_err, units_err] = derivnum ('phos',PAR1(PO4_valid),PAR2(PO4_valid),PAR1TYPE(PO4_valid),PAR2TYPE(PO4_valid),... 363 | SAL(PO4_valid),TEMPIN(PO4_valid),TEMPOUT(PO4_valid),PRESIN(PO4_valid),PRESOUT(PO4_valid),... 364 | SI(PO4_valid),PO4(PO4_valid),NH4(PO4_valid),H2S(PO4_valid),pHSCALEIN(PO4_valid),K1K2CONSTANTS(PO4_valid),KSO4CONSTANT(PO4_valid),... 365 | KFCONSTANT(PO4_valid),BORON(PO4_valid)); 366 | err = bsxfun(@times,deriv,ePO4(PO4_valid)); 367 | new_size = [ntps size(err,2)]; 368 | sq_err = zeros(new_size) + sq_err; 369 | sq_err(PO4_valid,:) = sq_err(PO4_valid,:) + err .* err; 370 | end 371 | 372 | % Contribution of Ammonia (total dissolved inorganic concentration) to squared standard error 373 | % 374 | % Remark : does not compute error where NH4 = 0 375 | % because computation of sensitivity to SI fails in that case 376 | % 377 | NH4_valid = (E & NH4 ~= 0 & eNH4 ~= 0); 378 | if (any (NH4_valid)) 379 | % Compute sensitivities (partial derivatives) 380 | [deriv, ~, ~, headers_err, units_err] = derivnum ('amm',PAR1(NH4_valid),PAR2(NH4_valid),PAR1TYPE(NH4_valid),PAR2TYPE(NH4_valid),... 381 | SAL(NH4_valid),TEMPIN(NH4_valid),TEMPOUT(NH4_valid),PRESIN(NH4_valid),PRESOUT(NH4_valid),... 382 | SI(NH4_valid),PO4(NH4_valid),NH4(NH4_valid),H2S(NH4_valid),pHSCALEIN(NH4_valid),K1K2CONSTANTS(NH4_valid),KSO4CONSTANT(NH4_valid),... 383 | KFCONSTANT(NH4_valid),BORON(NH4_valid)); 384 | err = bsxfun(@times,deriv,eNH4(NH4_valid)); 385 | new_size = [ntps size(err,2)]; 386 | sq_err = zeros(new_size) + sq_err; 387 | sq_err(NH4_valid,:) = sq_err(NH4_valid,:) + err .* err; 388 | end 389 | 390 | % Contribution of Hydrogen Sulfide (total dissoloved inorganic concentration) to squared standard error 391 | % 392 | % Remark : does not compute error where H2S = 0 393 | % because computation of sensitivity to PO4 fails in that case 394 | % 395 | H2S_valid = (E & H2S ~= 0 & eH2S ~= 0); 396 | if (any (H2S_valid)) 397 | % Compute sensitivities (partial derivatives) 398 | [deriv, ~, ~, headers_err, units_err] = derivnum ('hyd',PAR1(H2S_valid),PAR2(H2S_valid),PAR1TYPE(H2S_valid),PAR2TYPE(H2S_valid),... 399 | SAL(H2S_valid),TEMPIN(H2S_valid),TEMPOUT(H2S_valid),PRESIN(H2S_valid),PRESOUT(H2S_valid),... 400 | SI(H2S_valid),PO4(H2S_valid),NH4(H2S_valid),H2S(H2S_valid),pHSCALEIN(H2S_valid),K1K2CONSTANTS(H2S_valid),KSO4CONSTANT(H2S_valid),... 401 | KFCONSTANT(H2S_valid),BORON(H2S_valid)); 402 | err = bsxfun(@times,deriv,eH2S(H2S_valid)); 403 | new_size = [ntps size(err,2)]; 404 | sq_err = zeros(new_size) + sq_err; 405 | sq_err(H2S_valid,:) = sq_err(H2S_valid,:) + err .* err; 406 | end 407 | 408 | % Contribution of T (temperature) to squared standard error 409 | if (any (eTEMP ~= 0.0)) 410 | % Compute sensitivities (partial derivatives) 411 | [deriv(E,:),~,~,headers_err,units_err] = derivnum ('T',... 412 | PAR1(E),PAR2(E),PAR1TYPE(E),PAR2TYPE(E),SAL(E),TEMPIN(E),... 413 | TEMPOUT(E),PRESIN(E),PRESOUT(E),SI(E),PO4(E),NH4(E),H2S(E),... 414 | pHSCALEIN(E),K1K2CONSTANTS(E),KSO4CONSTANT(E),KFCONSTANT(E),BORON(E)); 415 | err = bsxfun(@times,deriv,eTEMP); 416 | sq_err = err*0. + sq_err; 417 | sq_err = sq_err + err .* err; 418 | end 419 | 420 | % Contribution of S (salinity) to squared standard error 421 | if (any (eSAL ~= 0.0)) 422 | % Compute sensitivities (partial derivatives) 423 | [deriv(E,:),~,~,headers_err,units_err] = derivnum ('S',... 424 | PAR1(E),PAR2(E),PAR1TYPE(E),PAR2TYPE(E),SAL(E),TEMPIN(E),... 425 | TEMPOUT(E),PRESIN(E),PRESOUT(E),SI(E),PO4(E),NH4(E),H2S(E),... 426 | pHSCALEIN(E),K1K2CONSTANTS(E),KSO4CONSTANT(E),KFCONSTANT(E),BORON(E)); 427 | err = bsxfun(@times,deriv,eSAL); 428 | sq_err = err*0. + sq_err; 429 | sq_err = sq_err + err .* err; 430 | end 431 | 432 | % Calculate dissociation constants 433 | data = CO2SYS(PAR1,PAR2,PAR1TYPE,PAR2TYPE,SAL,TEMPIN,TEMPOUT,PRESIN,PRESOUT,SI,PO4,NH4,H2S,pHSCALEIN,K1K2CONSTANTS,KSO4CONSTANT,KFCONSTANT,BORON); 434 | 435 | % Calculate [Ca++] 436 | % ' Riley, J. P. and Tongudai, M., Chemical Geology 2:263-269, 1967: 437 | % ' this is .010285.*Sali./35 438 | Ca = 0.02128./40.087.*(SAL./1.80655);% ' in mol/kg-SW 439 | 440 | % Contribution of all pKi to squared standard error 441 | for i = 1:length(epK) 442 | 443 | % if error on Ki is given 444 | if (epK(i) ~= 0.0) 445 | % Select Ki 446 | switch i 447 | case 1 448 | Ki_in = data(:,63); % K0in 449 | Ki_out = data(:,78); % K0out 450 | case 2 451 | Ki_in = data(:,64); % K1in 452 | Ki_out = data(:,79); % K1out 453 | case 3 454 | Ki_in = data(:,65); % K2in 455 | Ki_out = data(:,80); % K2out 456 | case 4 457 | Ki_in = data(:,69); % KBin 458 | Ki_out = data(:,84); % KBout 459 | case 5 460 | Ki_in = data(:,68); % KWin 461 | Ki_out = data(:,83); % KWout 462 | case 6 463 | % Recompute KAr from OmegaAr and ions [Ca++] and [CO3--] concentrations 464 | OmegaAr = data(:,18); 465 | CO3 = data(:,7) * 1e-6; 466 | Ki_in = CO3.*Ca./OmegaAr; % KspAin 467 | Ki_in(isnan(Ki_in)) = 0; 468 | OmegaAr = data(:,36); 469 | CO3 = data(:,25) * 1e-6; 470 | Ki_out = CO3.*Ca./OmegaAr; % KspAout 471 | Ki_out(isnan(Ki_out)) = 0; 472 | case 7 473 | % Recompute KCa from OmegaCa and ions [Ca++] and [CO3--] concentrations 474 | OmegaCa = data(:,17); 475 | CO3 = data(:,7) * 1e-6; 476 | Ki_in = CO3.*Ca./OmegaCa; % KspCin 477 | Ki_in(isnan(Ki_in)) = 0; 478 | OmegaCa = data(:,35); 479 | CO3 = data(:,25) * 1e-6; 480 | Ki_out = CO3.*Ca./OmegaCa; % KspCout 481 | Ki_out(isnan(Ki_out)) = 0; 482 | end 483 | 484 | % compute error on Ki from that on pKi 485 | eKi_in = - epK(i) * Ki_in * log(10); % error on K at input conditions 486 | eKi_out = - epK(i) * Ki_out * log(10); % error on K at output conditions 487 | % Compute sensitivities (partial derivatives) 488 | [deriv(E,:),~,~,headers_err,units_err] = derivnum (cell2mat(Knames(1,i)),... 489 | PAR1(E),PAR2(E),PAR1TYPE(E),PAR2TYPE(E),... 490 | SAL(E),TEMPIN(E),TEMPOUT(E),PRESIN(E),PRESOUT(E),... 491 | SI(E),PO4(E),NH4(E),H2S(E),pHSCALEIN(E),K1K2CONSTANTS(E),... 492 | KSO4CONSTANT(E),KFCONSTANT(E),BORON(E)); 493 | % Derivatives at input conditions are calculated with respect 494 | % to constants at input conditions, and derivatives at output 495 | % conditions are calculated with respect to constants at output 496 | % conditions. So, error terms are calculated using absolute 497 | % errors in K values at both input and output conditions. 498 | err(:, 1:12) = bsxfun(@times, deriv(:, 1:12), eKi_in); 499 | err(:, 13:22) = bsxfun(@times, deriv(:, 13:22), eKi_out); 500 | sq_err = err*0. + sq_err; 501 | sq_err = sq_err + err .* err; 502 | end 503 | end 504 | 505 | % Contribution of Boron (total dissoloved boron concentration) to squared standard error 506 | if (eBt ~= 0) 507 | % Compute sensitivities (partial derivatives) 508 | [deriv(E,:),~,~,headers_err,units_err] = derivnum ('bor',... 509 | PAR1(E),PAR2(E),PAR1TYPE(E),PAR2TYPE(E),SAL(E),TEMPIN(E),... 510 | TEMPOUT(E),PRESIN(E),PRESOUT(E),SI(E),PO4(E),NH4(E),H2S(E),... 511 | pHSCALEIN(E),K1K2CONSTANTS(E),KSO4CONSTANT(E),KFCONSTANT(E),BORON(E)); 512 | err = bsxfun(@times, deriv, (eBt*data(:,93)*1e-6)); % where TB = data(:,93) in umol B/kg 513 | new_size = [ntps size(err,2)]; 514 | sq_err = zeros(new_size) + sq_err; 515 | sq_err = sq_err + err .* err; 516 | end 517 | 518 | % Contribution of Calcium (total dissoloved calcium concentration) to squared standard error 519 | CAL_valid = (E & eCAL ~= 0); 520 | if (any (CAL_valid)) 521 | % Compute sensitivities (partial derivatives) 522 | [deriv,~,~,headers_err,units_err] = derivnum ('cal',... 523 | PAR1(CAL_valid),PAR2(CAL_valid),PAR1TYPE(CAL_valid),PAR2TYPE(CAL_valid),SAL(CAL_valid),TEMPIN(CAL_valid),... 524 | TEMPOUT(CAL_valid),PRESIN(CAL_valid),PRESOUT(CAL_valid),SI(CAL_valid),PO4(CAL_valid),NH4(CAL_valid),H2S(CAL_valid),... 525 | pHSCALEIN(CAL_valid),K1K2CONSTANTS(CAL_valid),KSO4CONSTANT(CAL_valid),KFCONSTANT(CAL_valid),BORON(CAL_valid)); 526 | err = bsxfun(@times,deriv,eCAL(CAL_valid)); 527 | new_size = [ntps size(err,2)]; 528 | sq_err = zeros(new_size) + sq_err; 529 | sq_err(CAL_valid,:) = sq_err(CAL_valid,:) + err .* err; 530 | end 531 | 532 | % Compute and return resulting total error (or uncertainty) 533 | total_error = sqrt (sq_err); 534 | total_error(isnan(total_error))=-999; 535 | headers = strcat('u(',headers_err,')'); 536 | units = strcat('(',units_err,')'); 537 | end 538 | -------------------------------------------------------------------------------- /main/example_CO2SYS.m: -------------------------------------------------------------------------------- 1 | %% Example CO2SYSv3 function run 2 | % 3 | % This example function uses CO2SYSv3 to calculate carbonate system 4 | % parameters using an input of total alkalinity and dissolved inorganic 5 | % carbon, and compares aragonite saturation states calculated at laboratory 6 | % conditions to aragonite saturation states calculated at in situ 7 | % conditions. 8 | 9 | % This is data from Station 156 of the P16N cruise in 2015 (44' N, 155' W) 10 | PRS = [3.4 40.6 69.5 109.1 174.6 250.0 332.7 432.4 550.1 701.0 850.9 1000.2 1167.2 1366.8 1599.9 1933.4 2366.6 2866.0 3366.6 3865.3 4367.8 4866.4 5282.4 5727.4]; 11 | TMP = [11.805 10.0835 9.0949 8.7028 8.1385 7.2186 6.0406 4.8193 4.2995 3.9305 3.4850 3.1329 2.8238 2.5597 2.3071 2.0166 1.7961 1.6101 1.5137 1.4757 1.4998 1.5522 1.6036 1.6633]; 12 | SAL = [33.095 33.0915 33.1315 33.7722 33.9339 33.942 33.9295 33.9477 34.053 34.1861 34.2706 34.3405 34.4022 34.4614 34.5131 34.5741 34.6161 34.6497 34.6675 34.6809 34.6850 34.6858 34.6859 34.6873]; 13 | SIL = [4.21 7.44 8.51 15.06 25.43 38.92 53.79 72.66 89.96 106.57 121.23 132.96 142.73 151.81 159.73 165.97 167.73 164.21 159.80 157.56 160.39 162.83 163.42 163.52]; 14 | PO4 = [0.61 0.74 0.84 1.05 1.38 1.76 2.16 2.57 2.82 2.97 3.06 3.12 3.11 3.11 3.08 3.00 2.87 2.73 2.62 2.55 2.52 2.52 2.52 2.51]; 15 | DIC = [2021.9 2028.9 2047.0 2096.7 2135.5 2179.2 2223.2 2269.0 2306.4 2338.4 2360.0 2375.0 2384.2 2393.9 2401.2 2387.7 2382.0 2362.0 2348.2 2338.6 2337.5 2337.1 2337.2 2338.8]; 16 | TA = [2216.6 2215.2 2216.7 2250.9 2261.1 2278.2 2280.7 2301.5 2320.7 2336.8 2355.3 2369.7 2381.0 2393.9 2405.2 2413.0 2420.8 2422.2 2423.4 2425.4 2429.8 2431.6 2431.0 2432.4]; 17 | 18 | % Define constants for carbonate system calculations 19 | SCALE = 1; % Total pH scale 20 | K1K2 = 10; % Leuker et al (2000) K1K2 21 | SO4 = 1; % Dickson (1990) KSO4 22 | KF = 2; % Perez & Fraga (1987) KF 23 | BOR = 2; % Lee et al (2010) TB 24 | 25 | % Performs carbonate system calculations 26 | C = CO2SYS(TA,DIC,1,2,SAL,20,TMP,0,PRS,SIL,PO4,0,0,SCALE,K1K2,... 27 | SO4,KF,BOR); 28 | 29 | % Plot Omega(Ar) 30 | OmA_in = C(:,18); 31 | OmA_out = C(:,35); 32 | figure; clf; hold on 33 | scatter(OmA_in, PRS,70,'Filled','Marker','o'); 34 | scatter(OmA_out,PRS,70,'Filled','Marker','^'); 35 | set(gcf,'Position',[200,200,600,600]); 36 | set(gca,'Fontsize',15); 37 | set(gca,'XAxisLocation', 'top') 38 | xlabel('\Omega_{Ar}','Fontsize',18); 39 | ylabel('Depth (dbar)'); 40 | legend('\Omega_{Ar,in}','\Omega_{Ar,out}','Location','southeast') 41 | set(gca,'Ydir','reverse'); % reverse y-axis direction 42 | hold off 43 | -------------------------------------------------------------------------------- /v2_0_5_compatible/derivnum_adjusted_to_v2_0_5.m: -------------------------------------------------------------------------------- 1 | % derivnum() 2 | % This subroutine computes partial derivatives of output carbonate variables 3 | % with respect to input variables (two), plus nutrients (two), ammonium and hydrogen sulfide, 4 | % temperature and salinity, dissociation constants, and total boron. 5 | % 6 | % It uses central differences, introducing a small perturbation 7 | % (plus and minus of a delta) in one INPUT variable and computes the 8 | % resulting induced change in each OUTPUT variable 9 | % 10 | % This subroutine has been modified from its original version (Orr et al. 11 | % 2018) to allow for input variables of CO2, HCO3, and CO3, the inclusion 12 | % of ammonium and hydrogen sulfide, and compatibility with CO2SYS_adjusted_to_v2_0_5.m(v3) 13 | % 14 | % After numerical tests, the small PERTURBATION (delta) is chosen 15 | % as a fraction of a reference value as follows: 16 | % * 1.e-3 (0.1%) for the equilibrium constants and total boron 17 | % * 1.e-6 for the pair of CO2 system input variables (PAR1, PAR2) 18 | % * 1.e-4 for temperature and salinity 19 | % * 1.e-3 for total dissolved inorganic P and Si (PO4, SI) 20 | % 21 | %************************************************************************** 22 | % 23 | % **** SYNTAX: 24 | % [deriv, headers_der, units_der, headers_err, units_err]=... 25 | % derivnum(VARID,PAR1,PAR2,PAR1TYPE,PAR2TYPE,... 26 | % SAL,TEMPIN,TEMPOUT,PRESIN,PRESOUT,... 27 | % SI,PO4,NH4,H2S,pHSCALEIN,K1K2CONSTANTS,... 28 | % KSO4CONSTANT,KFCONSTANT,BORON) 29 | % 30 | % **** SYNTAX EXAMPLES: 31 | % [der, headers, units] = derivnum('par1',2400,2200,1,2,35,0,25,4200,0,15,1,0,0,1,4,1,1,1) 32 | % [der, headers, units] = derivnum('sit', 2400, 8,1,3,35,0,25,4200,0,15,1,0,0,1,4,1,1,1) 33 | % [deriv, headers] = derivnum( 'T', 500, 8,5,3,35,0,25,4200,0,15,1,0,0,1,4,1,1,1) 34 | % [deriv] = derivnum('S',2400,2000:10:2400,1,2,35,0,25,4200,0,15,1,0,0,1,4,1,1,1) 35 | % [deriv] = derivnum('K0',2400,2200,1,2,0:1:35,0,25,4200,0,15,1,0,0,1,4,1,1,1) 36 | % [deriv] = derivnum('K1',2400,2200,1,2,35,0,25,0:100:4200,0,15,1,0,0,1,4,1,1,1) 37 | % 38 | %************************************************************************** 39 | % 40 | % INPUT: 41 | % 42 | % - VARID : character string to select the input variable for which 43 | % derivatives will be taken with respect to. 44 | % This variable appears in denominator of each resulting derivative. 45 | % = variable length, case insensitive, character code 46 | % case 'par1' : Parameter 1 of the input pair (This is TAlk if PAR1TYPE is 1) 47 | % case 'par2' : Parameter 2 of the input pair (This is TAlk if PAR2TYPE is 1) 48 | % case 'sil', 'silt', 'tsil', 'silicate', or 'sit' : Silicate concentration 49 | % case 'phos', 'phost', 'tphos', 'phosphate', or 'pt' : Phosphate concentration 50 | % case 'amm', 'nh4', 'NH4' : Ammonia concentration 51 | % case 'hyd', 'h2s', 'H2S' : Hydrogen Sulfide concentration 52 | % case 't', 'temp' or 'temperature' : temperature 53 | % case 's', 'sal', or 'salinity' : salinity 54 | % case 'K0','K1','K2','Kb','Kw','Kspa', 'Kspc': dissociation constants 55 | % case 'bor': total boron 56 | % 57 | % - all others : same list of input parameters as in CO2SYS_adjusted_to_v2_0_5() subroutine (version 3, scalar or vectors) 58 | % 59 | %**************************************************************************% 60 | % 61 | % OUTPUT: 3 arrays 62 | % a) an array containing the derivative of the following parameter (one row per sample): 63 | % b) the corresponding cell-array containing crudely formatted headers 64 | % c) the corresponding cell-array containing the units 65 | % 66 | % POS PARAMETER UNIT 67 | % 68 | % 01 - TAlk (umol/kgSW) 69 | % 02 - TCO2 (umol/kgSW) 70 | % 03 - [H+] in (umol/kgSW) 71 | % 04 - pCO2 in (uatm) 72 | % 05 - fCO2 in (uatm) 73 | % 06 - HCO3 in (umol/kgSW) 74 | % 07 - CO3 in (umol/kgSW) 75 | % 08 - CO2 in (umol/kgSW) 76 | % 09 - RF in () 77 | % 10 - OmegaCa in () 78 | % 11 - OmegaAr in () 79 | % 12 - xCO2 in (ppm) 80 | % 13 - [H+] out () 81 | % 14 - pCO2 out (uatm) 82 | % 15 - fCO2 out (uatm) 83 | % 16 - HCO3 out (umol/kgSW) 84 | % 17 - CO3 out (umol/kgSW) 85 | % 18 - CO2 out (umol/kgSW) 86 | % 19 - RF out () 87 | % 20 - OmegaCa out () 88 | % 21 - OmegaAr out () 89 | % 22 - xCO2 out (ppm) 90 | % 91 | % * 'in' refers to INPUT conditions (TEMPIN, PRESIN) as for CO2SYS 92 | % 'out' refers to OUTPUT conditions (TEMPOUT, PRESOUT) 93 | % 94 | % Note: derivnum does not take derivatives of the two CO2 system input vars. 95 | % Hence 2 out of the first 8 results listed above will be omitted. 96 | % The index (POS) will be shifted accordingly 97 | % (always beginning at 1 and ending at 18): 98 | % * with the TAlk-TCO2 input pair, POS=1 corresponds to ([H+]in)'; 99 | % * with the TAlk-pCO2 pair, POS = 1,2,3 are (TCO2in)', ([H+]in)', (fCO2in)'; 100 | % * POS 18 is always for (xCO2out)'. 101 | % 102 | % CAUTION: derivnum.m is NOT designed to take partial derivatives of 103 | % input vars, only computed variables relative to input 104 | % variables. However, those partial derivatives of input vars are 105 | % kept in the OUT conditions to maintain consistency 106 | % of the order of output between the different input pairs. 107 | % Derivatives of input vars are included when pH, pCO2, or fCO2 is 108 | % a member of the input pair (e.g., pH-Alk, pCO2-DIC, or 109 | % pH-pCO2). These partial derivatives of input vars are 110 | % not used by the "errors.m" function (for uncertainty propagation). 111 | % Generally, we advise not to use these derivatives of input 112 | % variables: in some cases their results appear accurate 113 | % while in other cases their results have been masked with 114 | % a NaN. Use them at your own risk. 115 | % 116 | function [derivatives, headers, units, headers_err, units_err] = ... 117 | derivnum_adjusted_to_v2_0_5 (VARID,PAR1,PAR2,PAR1TYPE,PAR2TYPE, SAL,TEMPIN, ... 118 | TEMPOUT,PRESIN,PRESOUT,SI,PO4,NH4,H2S, ... 119 | pHSCALEIN,K1K2CONSTANTS,KSO4CONSTANT, ... 120 | KFCONSTANT,BORON) 121 | 122 | % For computing derivative with respect to Ks, one has to call CO2sys with a perturbed K 123 | % Requested perturbation is passed through the following global variables 124 | global PertK % Id of perturbed K 125 | global Perturb % perturbation 126 | 127 | % Input conditioning 128 | % ------------------ 129 | 130 | VARID = upper(VARID); 131 | % Determine lengths of input vectors 132 | veclengths=[length(PAR1) length(PAR2) length(PAR1TYPE)... 133 | length(PAR2TYPE) length(SAL) length(TEMPIN)... 134 | length(TEMPOUT) length(PRESIN) length(PRESOUT)... 135 | length(SI) length(PO4) length(NH4) length(H2S)... 136 | length(pHSCALEIN) length(K1K2CONSTANTS)... 137 | length(KSO4CONSTANT) length(KFCONSTANT)... 138 | length(BORON)]; 139 | 140 | if length(unique(veclengths))>2 141 | disp(' '); disp('*** INPUT ERROR: Input vectors must all be of same length, or of length 1. ***'); disp(' '); return 142 | end 143 | 144 | % Make column vectors of all input vectors 145 | PAR1 =PAR1 (:); 146 | PAR2 =PAR2 (:); 147 | PAR1TYPE =PAR1TYPE (:); 148 | PAR2TYPE =PAR2TYPE (:); 149 | SAL =SAL (:); 150 | TEMPIN =TEMPIN (:); 151 | TEMPOUT =TEMPOUT (:); 152 | PRESIN =PRESIN (:); 153 | PRESOUT =PRESOUT (:); 154 | SI =SI (:); 155 | PO4 =PO4 (:); 156 | NH4 =NH4 (:); 157 | H2S =H2S (:); 158 | pHSCALEIN =pHSCALEIN (:); 159 | K1K2CONSTANTS=K1K2CONSTANTS(:); 160 | KSO4CONSTANT =KSO4CONSTANT (:); 161 | KFCONSTANT =KFCONSTANT (:); 162 | BORON =BORON (:); 163 | 164 | % Find the longest column vector: 165 | ntps = max(veclengths); 166 | 167 | % Populate column vectors 168 | PAR1(1:ntps,1) = PAR1(:) ; 169 | PAR2(1:ntps,1) = PAR2(:) ; 170 | PAR1TYPE(1:ntps,1) = PAR1TYPE(:) ; 171 | PAR2TYPE(1:ntps,1) = PAR2TYPE(:) ; 172 | SAL(1:ntps,1) = SAL(:) ; 173 | TEMPIN(1:ntps,1) = TEMPIN(:) ; 174 | TEMPOUT(1:ntps,1) = TEMPOUT(:) ; 175 | PRESIN(1:ntps,1) = PRESIN(:) ; 176 | PRESOUT(1:ntps,1) = PRESOUT(:) ; 177 | SI(1:ntps,1) = SI(:) ; 178 | PO4(1:ntps,1) = PO4(:) ; 179 | SI(1:ntps,1) = SI(:) ; 180 | PO4(1:ntps,1) = PO4(:) ; 181 | NH4(1:ntps,1) = NH4(:) ; 182 | H2S(1:ntps,1) = H2S(:) ; 183 | pHSCALEIN(1:ntps,1) = pHSCALEIN(:) ; 184 | K1K2CONSTANTS(1:ntps,1) = K1K2CONSTANTS(:) ; 185 | KSO4CONSTANT(1:ntps,1) = KSO4CONSTANT(:) ; 186 | KFCONSTANT(1:ntps,1) = KFCONSTANT(:) ; 187 | BORON(1:ntps,1) = BORON(:) ; 188 | 189 | % BASELINE: 190 | % -------- 191 | 192 | carb = CO2SYS_adjusted_to_v2_0_5(PAR1,PAR2,PAR1TYPE,PAR2TYPE,SAL,TEMPIN,TEMPOUT,PRESIN,PRESOUT,SI,PO4,NH4,H2S,pHSCALEIN,K1K2CONSTANTS,KSO4CONSTANT,KFCONSTANT,BORON); 193 | % Compute [H+] in µmol/KgSW 194 | if (ndims(carb) == 2) 195 | Hin = 10.^(-carb(:,3)) * 1.e6; 196 | Hout = 10.^(-carb(:,21)) * 1.e6; 197 | carb = horzcat(carb(:,1:2), Hin, carb(:,3:20), Hout, carb(:,21:end)); 198 | else 199 | Hin = 10.^(-carb(3)) * 1.e6; 200 | Hout = 10.^(-carb(21)) * 1.e6; 201 | carb = horzcat(carb(1:2), Hin, carb(3:20), Hout, carb(21:end)); 202 | end 203 | 204 | % Compute two slightly different values for input 205 | % ----------------------------------------------- 206 | 207 | % Default values : not perturbed 208 | % no change in PAR1 and PAR2 209 | PAR11 = PAR1; PAR12 = PAR1; 210 | PAR21 = PAR2; PAR22 = PAR2; 211 | % no change in Sil total and Phos total (except for d/dSi, d/dPO4) 212 | SI1 = SI; SI2 = SI; 213 | PO41 = PO4; PO42 = PO4; 214 | % no change in Amm total and H2S total (except for d/dAmm, d/dH2S) 215 | NH41 = NH4; NH42 = NH4; 216 | H2S1 = H2S; H2S2 = H2S; 217 | % no change in T and S (except for d/dS and d/dT, respectively) 218 | TEMP1 = TEMPIN; TEMP2 = TEMPIN; 219 | SAL1 = SAL; SAL2 = SAL; 220 | % no change in TEMPOUT except for d/dT (see why further below) 221 | TEMPOUT1 = TEMPOUT; TEMPOUT2 = TEMPOUT; 222 | 223 | % Create empty vector for abs_dx (absolute delta) 224 | abs_dx = nan(ntps,1); 225 | 226 | % Flag for dissociation constant as perturbed variable 227 | flag_dissoc_K = 0; % False 228 | % names of 7 dissociation constants and variable for total boron 229 | K_names = {'K0', 'K1', 'K2', 'KB', 'KW', 'KSPA', 'KSPC', 'BOR', 'CAL'}; 230 | 231 | % Units for derivatives and for errors 232 | units_at = {'umol';'umol';'nmol';'total scale';'uatm kg';'uatm kg';'umol';'umol';... 233 | 'umol';'kg';'kg';'kg';'ppm kg';... 234 | 'nmol';'uatm kg';'uatm kg';'umol';'umol';... 235 | 'umol';'kg';'kg';'kg';'ppm kg'; 236 | }; 237 | 238 | units_kg = {'umol/kg';'umol/kg';'nmol/kg';'total scale';'uatm';'uatm';'umol/kg';'umol/kg';... 239 | 'umol/kg';' ';' ';' ';'ppm';... 240 | 'nmol/kg';'uatm';'uatm';'umol/kg';'umol/kg';... 241 | 'umol/kg';' ';' ';' ';'ppm'; 242 | }; 243 | 244 | units_k = units_kg; 245 | 246 | units_pco2 = units_kg; 247 | 248 | units_err = units_kg; 249 | 250 | switch VARID 251 | case K_names 252 | flag_dissoc_K = 1; 253 | % Approximate values for K0, K1, K2, Kb, Kspa, Kspc, and CAL 254 | % They will be used to compute an absolute perturbation 255 | % value on these constants 256 | K = [0.034, 1.2e-06, 8.3e-10, 2.1e-09, 6.1e-14, 6.7e-07, ... 257 | 4.3e-07, 0.0004157, 0.0103]; 258 | % Choose value of absolute perturbation 259 | [is_in_K_names, index] = ismember(VARID, K_names); 260 | perturbation = K(index) * 1.e-3; % 0.1 percent of Kx value 261 | abs_dx = 2 * perturbation; 262 | denom_headers = VARID; 263 | denom_units = ' '; 264 | units = units_k; 265 | 266 | case {'PAR1', 'VAR1'} % PAR1 (first variable of input pair) is perturbed 267 | % Define a relative delta 268 | delta = 1.e-6; 269 | 270 | PAR1ref = nan(size(PAR1TYPE)); 271 | units = units_kg; 272 | denom_headers = ''; 273 | denom_units = ''; 274 | t1=PAR1TYPE==1; 275 | PAR1ref(t1) = 2300.; % umol/kg (global surface average, Orr et al., 2017) 276 | t2=PAR1TYPE==2; 277 | PAR1ref(t2) = 2000.; % umol/kg (global surface average, Orr et al., 2017) 278 | t3=PAR1TYPE==3; 279 | PAR1ref(t3) = 1.0e-8; % mol/kg (equivalent to pH=8.0) 280 | t4=PAR1TYPE==4; 281 | PAR1ref(t4) = 400.; % uatm 282 | t5=PAR1TYPE==5; 283 | PAR1ref(t5) = 400.; % uatm 284 | t6=PAR1TYPE==6; 285 | PAR1ref(t6) = 1790.; % umol/kg 286 | t7=PAR1TYPE==7; 287 | PAR1ref(t7) = 200.; % umol/kg 288 | t8=PAR1TYPE==8; 289 | PAR1ref(t8) = 10.; % umol/kg 290 | 291 | % cases where first input variable is pH 292 | F = (PAR1TYPE == 3); 293 | H = 10.^(-PAR1(F)) ; % [H+] in mol/kg 294 | % Change slightly [H+] 295 | H1 = H - PAR1ref(F)*delta; 296 | H2 = H + PAR1ref(F)*delta; 297 | PAR11(F) = -log10(H1) ; 298 | PAR12(F) = -log10(H2) ; 299 | abs_dx(F) = (H2 - H1) * 1e9; % now in nmol/kg 300 | 301 | G = ~F; 302 | % Change slightly PAR1 303 | PAR11(G) = PAR1(G) - PAR1ref(G)*delta; 304 | PAR12(G) = PAR1(G) + PAR1ref(G)*delta; 305 | abs_dx(G) = PAR12(G) - PAR11(G); 306 | 307 | case {'PAR2', 'VAR2'} % PAR2 (second variable of input pair) is perturbed 308 | % Define a relative delta 309 | delta = 1.e-6; 310 | 311 | PAR2ref = nan(size(PAR2TYPE)); 312 | units = units_kg; 313 | denom_headers = ''; 314 | denom_units = ''; 315 | t1=PAR2TYPE==1; 316 | PAR2ref(t1) = 2300.; % umol/kg (global surface average, Orr et al., 2017) 317 | t2=PAR2TYPE==2; 318 | PAR2ref(t2) = 2000.; % umol/kg (global surface average, Orr et al., 2017) 319 | t3=PAR2TYPE==3; 320 | PAR2ref(t3) = 1.0e-8; % mol/kg (equivalent to pH=8.0) 321 | t4=PAR2TYPE==4; 322 | PAR2ref(t4) = 400.; % uatm 323 | t5=PAR2TYPE==5; 324 | PAR2ref(t5) = 400.; % uatm 325 | t6=PAR2TYPE==6; 326 | PAR2ref(t6) = 1790.; % umol/kg 327 | t7=PAR2TYPE==7; 328 | PAR2ref(t7) = 200.; % umol/kg 329 | t8=PAR2TYPE==8; 330 | PAR2ref(t8) = 10.; % umol/kg 331 | 332 | 333 | % cases where second input variable is pH 334 | F = (PAR2TYPE == 3); 335 | H = 10.^(-PAR2(F)) ; % H+ in mol/kg 336 | % Change slightly [H+] 337 | H1 = H - PAR2ref(F)*delta; 338 | H2 = H + PAR2ref(F)*delta; 339 | PAR21(F) = -log10(H1) ; 340 | PAR22(F) = -log10(H2) ; 341 | abs_dx(F) = (H2 - H1) * 1e9; 342 | 343 | G = ~F; 344 | % Change slightly PAR2 345 | PAR21(G) = PAR2(G) - PAR2ref(G)*delta; 346 | PAR22(G) = PAR2(G) + PAR2ref(G)*delta; 347 | abs_dx(G) = PAR22(G) - PAR21(G); 348 | 349 | case {'SIL', 'TSIL', 'SILT', 'SILICATE', 'SIT'} % Sil total 350 | % Define a relative delta 351 | delta = 1.e-3; 352 | SIref = 7.5; % umol/kg (global surface average, Orr et al., 2018) 353 | % Change slightly SI 354 | SI1 = SI - SIref*delta; 355 | SI2 = SI + SIref*delta; 356 | abs_dx = SI2 - SI1; 357 | denom_headers = 'Sit'; 358 | denom_units = 'umol'; 359 | units = units_at; 360 | case {'PHOS', 'TPHOS', 'PHOST', 'PHOSPHATE', 'PT'} % Phos total 361 | % Define a relative delta 362 | delta = 1.e-3; 363 | PO4ref = 0.5; % umol/kg (global surface average, Orr et al., 2018) 364 | % Change slightly PO4 365 | PO41 = PO4 - PO4ref*delta; 366 | PO42 = PO4 + PO4ref*delta; 367 | abs_dx = PO42 - PO41; 368 | denom_headers = 'Pt'; 369 | denom_units = 'umol'; 370 | units = units_at; 371 | case {'AMM','NH4'} % Amm total 372 | % Define a relative delta 373 | delta = 1.e-3; 374 | NH4ref = 1; 375 | % Change slightly NH4 376 | NH41 = NH4 - NH4ref*delta; 377 | NH42 = NH4 + NH4ref*delta; 378 | abs_dx = NH42 - NH41; 379 | denom_headers = 'NH4t'; 380 | denom_units = 'umol'; 381 | units = units_at; 382 | case {'HYD','H2S'} % H2S total 383 | % Define a relative delta 384 | delta = 1.e-3; 385 | H2Sref = 1; 386 | % Change slightly H2S 387 | H2S1 = H2S - H2Sref*delta; 388 | H2S2 = H2S + H2Sref*delta; 389 | abs_dx = H2S2 - H2S1; 390 | denom_headers = 'H2St'; 391 | denom_units = 'umol'; 392 | units = units_at; 393 | case {'T', 'TEMP', 'TEMPERATURE'} % Temperature 394 | % Define a relative delta 395 | delta = 1.e-4; 396 | TEMPref = 18.; % global surface mean (C) 397 | % Change slightly temperature 398 | TEMP1 = TEMPIN - TEMPref*delta; 399 | TEMP2 = TEMPIN + TEMPref*delta; 400 | abs_dx = TEMP2 - TEMP1; 401 | % When computing d/dT 402 | % TEMPOUT changes must be the same as TEMPIN changes, e.g, for 403 | % derivnum 'OUT' & 'IN' results to be the same when TEMPOUT=TEMPIN 404 | TEMPOUT1 = TEMPOUT - TEMPref*delta; 405 | TEMPOUT2 = TEMPOUT + TEMPref*delta; 406 | denom_headers = 'T'; 407 | denom_units = 'C'; 408 | units = units_kg; 409 | case {'S', 'SAL', 'SALINITY'} % Salinity 410 | % Define a relative delta 411 | delta = 1.e-4; 412 | SALref = 35.; 413 | % Change slightly salinity 414 | SAL1 = SAL - SALref*delta; 415 | SAL2 = SAL + SALref*delta; 416 | abs_dx = SAL2 - SAL1; 417 | denom_headers = 'S'; 418 | denom_units = 'psu'; 419 | units = units_kg; 420 | end 421 | 422 | % PERTURBATION: 423 | % ------------- 424 | 425 | % Point 1: (one dissociation constant or PAR1, PAR2, T or S is somewhat smaller) 426 | % if perturbed variable is a dissociation constant 427 | if (flag_dissoc_K) 428 | PertK = upper(VARID); 429 | Perturb = -perturbation; 430 | end 431 | cdel1 = CO2SYS_adjusted_to_v2_0_5 ( ... 432 | PAR11,PAR21,PAR1TYPE,PAR2TYPE,SAL1,TEMP1,TEMPOUT1,PRESIN,PRESOUT,SI1,PO41,NH41,H2S1,pHSCALEIN,K1K2CONSTANTS,KSO4CONSTANT,KFCONSTANT,BORON); 433 | % Compute [H+] 434 | if (ndims(cdel1) == 2) 435 | Hin = 10.^(-cdel1(:,3)) * 1.e9; % to show H+ results in nmol/kg 436 | Hout = 10.^(-cdel1(:,21)) * 1.e9; 437 | cdel1 = horzcat(cdel1(:,1:2), Hin, cdel1(:,3:20), Hout, cdel1(:,21:end)); 438 | else 439 | Hin = 10.^(-cdel1(3)) * 1.e9; 440 | Hout = 10.^(-cdel1(21)) * 1.e9; 441 | cdel1 = horzcat(cdel1(1:2), Hin, cdel1(3:20), Hout, cdel1(21:end)); 442 | end 443 | 444 | % Point 2: (one dissociation constant or PAR1, PAR2, T or S is somewhat bigger) 445 | % if perturbed variable is a dissociation constant 446 | if (flag_dissoc_K) 447 | PertK = upper(VARID); 448 | Perturb = perturbation; 449 | end 450 | cdel2 = CO2SYS_adjusted_to_v2_0_5 ( ... 451 | PAR12,PAR22,PAR1TYPE,PAR2TYPE,SAL2,TEMP2,TEMPOUT2,PRESIN,PRESOUT,SI2,PO42,NH42,H2S2,pHSCALEIN,K1K2CONSTANTS,KSO4CONSTANT,KFCONSTANT,BORON); 452 | % Compute [H+] 453 | if (ndims(cdel2) == 2) 454 | % Computed variable H+ (does not affect other computed 455 | % variables, i.e., it is the numerator of the derivative) 456 | Hin = 10.^(-cdel2(:,3)) * 1.e9; % to show H+ results in nmol/kg 457 | Hout = 10.^(-cdel2(:,21)) * 1.e9; 458 | cdel2 = horzcat(cdel2(:,1:2), Hin, cdel2(:,3:20), Hout, cdel2(:,21:end)); 459 | else 460 | Hin = 10.^(-cdel2(3)) * 1.e9; 461 | Hout = 10.^(-cdel2(21)) * 1.e9; 462 | cdel2 = horzcat(cdel2(1:2), Hin, cdel2(3:20), Hout, cdel2(21:end)); 463 | end 464 | 465 | % if perturbed variable is a dissociation constant 466 | if (flag_dissoc_K) 467 | PertK = ''; %% Return to normal 468 | end 469 | 470 | % Drop unnecessary columns 471 | % Note: columns (04 - pHin) and (20 - pHout) drop 472 | % Keep only the following columns 473 | % 01 - TAlk (umol/kgSW) 474 | % 02 - TCO2 (umol/kgSW) 475 | % 03 - [H+] in (nmol/kgSW) lastly added 476 | % 05 - pCO2 in (uatm) 477 | % 06 - fCO2 in (uatm) 478 | % 07 - HCO3 in (umol/kgSW) 479 | % 08 - CO3 in (umol/kgSW) 480 | % 09 - CO2 in (umol/kgSW) 481 | % 17 - RF in () 482 | % 18 - OmegaCa in () 483 | % 19 - OmegaAr in () 484 | % 20 - xCO2 in (ppm) 485 | % 22 - [H+] out (nmol/kgSW) lastly added 486 | % 24 - pCO2 out (uatm) 487 | % 25 - fCO2 out (uatm) 488 | % 26 - HCO3 out (umol/kgSW) 489 | % 27 - CO3 out (umol/kgSW) 490 | % 28 - CO2 out (umol/kgSW) 491 | % 36 - RF out () 492 | % 37 - OmegaCa out () 493 | % 38 - OmegaAr out () 494 | % 39 - xCO2 out (ppm) 495 | keep = [1 2 3 5 6 7 8 9 17 18 19 20 22 24 25 26 27 28 36 37 38 39]; 496 | 497 | % We will drop also some column headers 498 | headers = {'TAlk';'TCO2';'Hin';'pHin';'pCO2in';'fCO2in';'HCO3in';'CO3in';... 499 | 'CO2in';'RFin';'OmegaCAin';'OmegaARin';'xCO2in';... 500 | 'Hout';'pCO2out';'fCO2out';'HCO3out';'CO3out';... 501 | 'CO2out';'RFout';'OmegaCAout';'OmegaARout';'xCO2out'}; 502 | headers_err = headers; 503 | %units = {'umol';'umol';'nmol';'total scale';'uatm';'uatm';'umol';'umol';... 504 | % 'umol';' ';' ';'ppm';... 505 | % 'nmol';'uatm';'uatm';'umol';'umol';... 506 | % 'umol';' ';' ';'ppm'; 507 | % }; 508 | % Initially, keep all headers except 'pHin' 509 | keep_head = [1:3 5:23]; 510 | 511 | % % if all parameter PAR1 are of the same type 512 | % if all(PAR1TYPE == PAR1TYPE(1)) 513 | % % Determine column number of PAR1 514 | % if PAR1TYPE(1) <= 3 % TAlk, TCO2 or pH 515 | % % By design of CO2sys, PARTYPE is equal to column number 516 | % col_number = PAR1TYPE(1); 517 | % else 518 | % % Because there is an extra column: [H+] 519 | % col_number = PAR1TYPE(1) + 1; 520 | % end 521 | % % Exclude input parameters PAR1 522 | % A = (keep ~= col_number); 523 | % keep = keep (A); 524 | % A = (keep_head ~= col_number); 525 | % keep_head = keep_head (A); 526 | % end 527 | % % if all parameter PAR2 are of the same type 528 | % if all(PAR2TYPE == PAR2TYPE(1)) 529 | % % Determine column number of PAR1 530 | % if PAR2TYPE(1) <= 3 % TAlk, TCO2 or pH 531 | % % By design of CO2sys, PARTYPE is equal to column number 532 | % col_number = PAR2TYPE(1); 533 | % else 534 | % % Because there is an extra column: [H+] 535 | % col_number = PAR2TYPE(1) + 1; 536 | % end 537 | % % Exclude input parameters PAR2 538 | % A = (keep ~= col_number); 539 | % keep = keep (A); 540 | % A = (keep_head ~= col_number); 541 | % keep_head = keep_head (A); 542 | % end 543 | 544 | cdel1 = cdel1(:,keep); 545 | cdel2 = cdel2(:,keep); 546 | 547 | headers = headers(keep_head); 548 | units = units(keep_head); 549 | 550 | headers_err = headers_err(keep_head); 551 | units_err = units_err(keep_head); 552 | 553 | % concatenate strings to give each header its proper form, a partial derivative 554 | headers = strcat('d', headers, '/', 'd', denom_headers); 555 | % concatenate in an analogous way for the units 556 | units = strcat('(',units, '/', denom_units,')'); 557 | 558 | % Centered difference 559 | dy = (cdel2 - cdel1); 560 | 561 | % Compute ratio dy/dx 562 | if (isscalar(abs_dx)) 563 | derivatives = dy ./ abs_dx; 564 | else 565 | derivatives = bsxfun(@rdivide, dy, abs_dx); 566 | end 567 | 568 | % % Mask values that should not be used with NaN (e.g., dHout/dT when PAR1 or PAR2 is pH) 569 | % switch VARID 570 | % case {'T', 'TEMP', 'TEMPERATURE'} 571 | % % For PAR1TYPE or PAR2TYPE = 3 (pH is input) make dHout/dT value a NaN 572 | % F = (PAR1TYPE==3 | PAR2TYPE==3); % either CO2 system input variable is pH 573 | % [is_in_headers, idx] = ismember('dHout/dT', headers); 574 | % if any(is_in_headers) 575 | % derivatives(F,idx) = NaN ; 576 | % end 577 | % 578 | % % For PAR1TYPE or PAR2TYPE = 4 or 5 (pCO2 or fCO2 is input) make relevant d/dT values NaNs 579 | % F = (PAR1TYPE==4 | PAR2TYPE==4); %when pCO2 or fCO2 is input var 580 | % % masknan = {'dfCO2in/dT' 'dxCO2in/dT' 'dpCO2out/dT' 'dfCO2out/dT' 'dxCO2out/dT'}; 581 | % masknan = {'dpCO2out/dT'}; 582 | % [is_in_headers, idx] = ismember(masknan, headers); 583 | % if any(is_in_headers) 584 | % derivatives(F,idx) = NaN ; 585 | % end 586 | % 587 | % % For PAR1TYPE or PAR2TYPE = 5 (fCO2 is input) make relevant d/dT values NaNs 588 | % F = (PAR1TYPE==5 | PAR2TYPE==5); %when fCO2 is input var 589 | % % masknan = {'dfCO2in/dT' 'dxCO2in/dT' 'dpCO2out/dT' 'dfCO2out/dT' 'dxCO2out/dT'}; 590 | % masknan = {'dfCO2out/dT'}; 591 | % [is_in_headers, idx] = ismember(masknan, headers); 592 | % if any(is_in_headers) 593 | % derivatives(F,idx) = NaN ; 594 | % end 595 | % 596 | % case {'PAR1', 'VAR1'} 597 | % % For pH-pCO2 or pH-fCO2 pair (PAR1 is pH) 598 | % F = (PAR1TYPE==3 & (PAR2TYPE==4 | PAR2TYPE==5)); 599 | % masknan = {'dHout/dH' 'dpCO2out/dH' 'dfCO2out/dH'}; 600 | % [is_in_headers, idx] = ismember(masknan, headers); 601 | % if any(is_in_headers) 602 | % derivatives(F,idx) = NaN ; 603 | % end 604 | % 605 | % case {'PAR2', 'VAR2'} 606 | % % For pCO2-pH or fCO2-pH pair (PAR2 is pH) 607 | % F = ((PAR1TYPE==4 | PAR1TYPE==5) & PAR2TYPE==3); 608 | % masknan = {'dHout/dH' 'dpCO2out/dH' 'dfCO2out/dH'}; 609 | % [is_in_headers, idx] = ismember(masknan, headers); 610 | % if any(is_in_headers) 611 | % derivatives(F,idx) = NaN ; 612 | % end 613 | % end 614 | 615 | end 616 | -------------------------------------------------------------------------------- /v2_0_5_compatible/errors_adjusted_to_v2_0_5.m: -------------------------------------------------------------------------------- 1 | % errors() 2 | % This subroutine propagates uncertainties for the marine carbonate chemistry calculations 3 | % from errors (or uncertainties) on inputs: 4 | % - pair of carbonate system variables 5 | % - nutrient (silicate and phosphate) concentrations 6 | % - concentrations of ammonium and hydrogen sulfide 7 | % - temperature and salinity 8 | % - calcium concentration (optional) 9 | % plus errors in dissociation constants pK0, pK1, pK2, pKb, pKw, pKspa, and pKspc as well as total boron 10 | % 11 | % It calls derivnum_adjusted_to_v2_0_5, which computes numerical derivatives, and then 12 | % it applies error propagation using the method of moments. 13 | % The latter is a general technique to estimate the 2nd moment of a variable z 14 | % (variance or standard deviation) based on a 1st-order approximation to z. 15 | % 16 | % This subroutine has been modified from its original version (Orr et al. 17 | % 2018) to allow for input variables of CO2, HCO3, and CO3, the inclusion 18 | % of ammonium and hydrogen sulfide, compatibility with CO2SYS.m(v3), and am 19 | % optional input of calcium concentration uncertainty. 20 | % 21 | %************************************************************************** 22 | % 23 | % **** SYNTAX: 24 | % [err, headers, units] = errors_adjusted_to_v2_0_5(PAR1,PAR2,PAR1TYPE,PAR2TYPE,... 25 | % SAL,TEMPIN,TEMPOUT,PRESIN,PRESOUT,SI,PO4,... 26 | % NH4,H2S,ePAR1,ePAR2,eSAL,eTEMP,eSI,ePO4,... 27 | % eNH4,eH2S,epK,eBt,r,pHSCALEIN,K1K2CONSTANTS,... 28 | % KSO4CONSTANT,KFCONSTANT,BORON,eCAL(optional)) 29 | % 30 | % **** SYNTAX EXAMPLES: 31 | % [Result] = errors_adjusted_to_v2_0_5(2400,2200,1,2,35,10,10,0,0,15,1,0,0,2,2,0.01,0.01,0,0,0,0,0,0,0,1,4,1,1,1) 32 | % [Result,Headers] = errors_adjusted_to_v2_0_5(2400, 8,1,3,35,25,5,0,3000,15,1,0,0,2,0.001,0,0,0,0,0,0,0,0,0,1,4,1,1,1) 33 | % [Result,Headers,Units] = errors_adjusted_to_v2_0_5(500, 8,5,3,35,25,5,0,4000,15,1,0,0,2,0.001,0,0,0,0,0,0,'','',0,1,4,1,1,1) 34 | % [A] = errors_adjusted_to_v2_0_5(2400,2000:10:2400,1,2,35,10,10,0,0,15,2,0,0,2,0,0,0,0,0,0,0,'','',0,1,4,1,1,1) 35 | % [A] = errors_adjusted_to_v2_0_5(2400,2200,1,2,0:1:35,0,25,4200,0,15,1,0,0,2,2,0,0,0,0,0,0,'','',0,1,4,1,1,1) 36 | % epK = [0.002, 0.0075, 0.015, 0.01, 0.01, 0.02, 0.02]; 37 | % eBt = 0.02; 38 | % [A, hdr, units] = errors_adjusted_to_v2_0_5(2400,2200,1,2,35,0,25,0:100:4200,0,15,1,0,0,2,2,0,0,0,0,0,0,epK,eBt,0,1,4,1,1,1) 39 | % 40 | %************************************************************************** 41 | % 42 | % INPUT: 43 | % 44 | % - ePAR1, ePAR2 : uncertainty of PAR1 and PAR2 of input pair of CO2 system variables 45 | % * Same units as PAR1 & PAR2, except 46 | % * as a fractional relative error for CO2, HCO3, and CO3 (eCO3=0.02 is a 2% error) 47 | % - eS, eT : uncertainty of Salinity and Temperature (same units as S and T) 48 | % - ePO4, eSI : uncertainty of Phosphate and Silicate total concentrations (same units as PO4 and SI [umol/kg]) 49 | % - eNH4, eH2S : uncertainty of Ammonia and Hydrogen Sulfide total concentrations (same units as NH4 and H2S [umol/kg]) 50 | % - epK : uncertainty of all seven dissociation constants (a vector) [pK units] 51 | % - eBt : uncertainty of total boron, given as fractional relative error (eBt=0.02 is a 2% error) 52 | % - r : correlation coefficient between PAR1 AND PAR2 (typicaly 0) 53 | % - others : same as input for subroutine CO2SYS() (version 3, scalar or vectors) 54 | % - eCAL : uncertainty of calcium [mmol/kg] determined by ratio with salinity 55 | % 56 | % All parameters may be scalars or vectors except epK and eBt. 57 | % * epK must be vector of 7 values : errors of [pK0, pK1, pK2, pKb, pKw, pKspa, pKspc]. 58 | % These errors are assumed to be the same for all rows of data. 59 | % These 7 values are in pK units 60 | % 61 | % if epK is empty (= ''), this routine specifies default values. 62 | % These default standard errors are : 63 | % pK0 : 0.002 64 | % pK1 : 0.0075 65 | % pK2 : 0.015 66 | % pKb : 0.01 boric acid 67 | % pKw : 0.01 water dissociation 68 | % pKspa : 0.02 solubility product of Aragonite 69 | % pKspc : 0.02 solubility product of Calcite 70 | % 71 | % * eBt is a scalar real number, fractional relative error (between 0.00 and 1.00) 72 | % for TB, where the default is eBt=0.02. It is assumed to be the same 73 | % for all rows of data. 74 | % 75 | % In constrast, ePAR1, ePAR2, eS, eT, ePO4, eSI, eNH4, and eH2S 76 | % - if vectors, are errors associated with each data point 77 | % - if scalars, are one error value associated to all data points 78 | % The same for parameter "r". 79 | % 80 | % If no value is input for eCAL, it will not be evaluated. 81 | % 82 | % If 'r' is nonzero with a value between -1.0 and 1.0, it indicates the correlation 83 | % between uncertainties of the input pair of carbonate system variables. 84 | % By default, 'r' is zero. However, for some pairs the user may want to specify a 85 | % different value. For example, measurements of pCO2 and pH are often anti-correlated. 86 | % The same goes for two other pairs: 'CO2 and CO3' and 'pCO2 and 87 | % CO3'. But even for these cases, care is needed when using non-zero values of 'r'. 88 | % 89 | % When the user propagates errors for an individual 90 | % measurement, 'r' should ALWAYS be zero if each member of the input pair is 91 | % measured independently. In this case, we are interested in the 92 | % correlation between the uncertainties in those measurements, not in 93 | % the correlation between the measurments themselves. Uncertainties from 94 | % those measurements are probably not correlated if they come from 95 | % different instruments. Conversely, if users are interested in the 96 | % error in the mean of a distribution of measurements (i.e., if they are 97 | % propagating standard errors instead of standard deviations), one 98 | % should then also account for the correlation between the measurements of 99 | % the two variables of the input pair. 100 | % 101 | % For input pairs where one member is pH, this 'errors' routine automatically 102 | % inverses the sign of 'r'. 103 | % That inversion is done because the associated derivatives are computed in terms of 104 | % the hydrogen ion concentration H+, not pH. Therefore for each of these 6 105 | % flags, if the user wants to compute 'r' that should be done (1) using 106 | % the H+ concentration instead of pH, and (2) the sign of that computed 'r' 107 | % should be inversed when passing it as an argument to this routine. 108 | % To express perfect anticorrelation with pH, the user should 109 | % use 'r=-1.0'. 110 | % 111 | %************************************************************************** 112 | % 113 | % OUTPUT: * an array containing uncertainty for the following variables 114 | % (one row per sample): 115 | % * a cell-array containing crudely formatted headers 116 | % 117 | % POS PARAMETER UNIT 118 | % 119 | % 01 - TAlk (umol/kgSW) 120 | % 02 - TCO2 (umol/kgSW) 121 | % 03 - [H+] in (nmol/kgSW) 122 | % 04 - pCO2 in (uatm) 123 | % 05 - fCO2 in (uatm) 124 | % 06 - HCO3 in (umol/kgSW) 125 | % 07 - CO3 in (umol/kgSW) 126 | % 08 - CO2 in (umol/kgSW) 127 | % 09 - RF in () 128 | % 10 - OmegaCa in () 129 | % 11 - OmegaAr in () 130 | % 12 - xCO2 in (ppm) 131 | % 13 - [H+] out (nmol/kgSW) 132 | % 14 - pCO2 out (uatm) 133 | % 15 - fCO2 out (uatm) 134 | % 16 - HCO3 out (umol/kgSW) 135 | % 17 - CO3 out (umol/kgSW) 136 | % 18 - CO2 out (umol/kgSW) 137 | % 19 - RF out () 138 | % 20 - OmegaCa out () 139 | % 21 - OmegaAr out () 140 | % 22 - xCO2 out (ppm) 141 | % 142 | % NOTE: Only uncertainties for the output variables are provided. 143 | % Hence 2 out of the first 8 results listed above will be omitted. 144 | % The index (POS) will be shifted accordingly 145 | % (always beginning at 1 and ending at 18): 146 | % * with the TAlk-TCO2 input pair, POS=1 corresponds to ([H+]in)'; 147 | % * with the TAlk-pCO2 pair, POS = 1,2,3 are (TCO2in)', ([H+]in)', (fCO2in)'; 148 | % * POS 18 is always for (xCO2out)'. 149 | 150 | function [total_error, headers, units] = ... 151 | errors_adjusted_to_v2_0_5 (PAR1, PAR2, PAR1TYPE, PAR2TYPE, SAL, TEMPIN, TEMPOUT, PRESIN, PRESOUT, SI, PO4,... 152 | NH4, H2S, ePAR1, ePAR2, eSAL, eTEMP, eSI, ePO4, eNH4, eH2S, epK, eBt, r, ... 153 | pHSCALEIN,K1K2CONSTANTS,KSO4CONSTANT,KFCONSTANT,BORON,eCAL) 154 | 155 | global K0 K1 K2 KW KB KF KS KP1 KP2 KP3 KSi KNH4 KH2S E; 156 | 157 | % Input conditioning 158 | % ------------------ 159 | 160 | % Handle optional calcium error input 161 | if (nargin < 30), eCAL=0; end 162 | 163 | % Determine lengths of input vectors 164 | veclengths=[length(PAR1) length(PAR2) length(PAR1TYPE)... 165 | length(PAR2TYPE) length(SAL) length(TEMPIN)... 166 | length(TEMPOUT) length(PRESIN) length(PRESOUT)... 167 | length(SI) length(PO4) length(NH4) length(H2S)... 168 | length(ePAR1) length(ePAR2) length(eSAL) length(eTEMP)... 169 | length(eSI) length(ePO4) length(eNH4) length(eH2S)... 170 | length(r) length(pHSCALEIN) length(K1K2CONSTANTS)... 171 | length(KSO4CONSTANT) length(KFCONSTANT) length(BORON)... 172 | length(eCAL)]; 173 | 174 | if length(unique(veclengths))>2 175 | disp(' '); disp('*** INPUT ERROR: Input vectors must all be of same length, or of length 1. ***'); disp(' '); return 176 | end 177 | 178 | % Make column vectors of all input vectors 179 | PAR1 =PAR1 (:); 180 | PAR2 =PAR2 (:); 181 | PAR1TYPE =PAR1TYPE (:); 182 | PAR2TYPE =PAR2TYPE (:); 183 | SAL =SAL (:); 184 | TEMPIN =TEMPIN (:); 185 | TEMPOUT =TEMPOUT (:); 186 | PRESIN =PRESIN (:); 187 | PRESOUT =PRESOUT (:); 188 | SI =SI (:); 189 | PO4 =PO4 (:); 190 | NH4 =NH4 (:); 191 | H2S =H2S (:); 192 | ePAR1 =ePAR1 (:); 193 | ePAR2 =ePAR2 (:); 194 | eSAL =eSAL (:); 195 | eTEMP =eTEMP (:); 196 | eSI =eSI (:); 197 | ePO4 =ePO4 (:); 198 | eNH4 =eNH4 (:); 199 | eH2S =eH2S (:); 200 | r =r (:); 201 | pHSCALEIN =pHSCALEIN (:); 202 | K1K2CONSTANTS=K1K2CONSTANTS(:); 203 | KSO4CONSTANT =KSO4CONSTANT (:); 204 | KFCONSTANT =KFCONSTANT (:); 205 | BORON =BORON (:); 206 | eCAL =eCAL (:); 207 | 208 | % Find the longest column vector: 209 | ntps = max(veclengths); 210 | 211 | % Populate column vectors 212 | PAR1(1:ntps,1) = PAR1(:) ; 213 | PAR2(1:ntps,1) = PAR2(:) ; 214 | PAR1TYPE(1:ntps,1) = PAR1TYPE(:) ; 215 | PAR2TYPE(1:ntps,1) = PAR2TYPE(:) ; 216 | SAL(1:ntps,1) = SAL(:) ; 217 | TEMPIN(1:ntps,1) = TEMPIN(:) ; 218 | TEMPOUT(1:ntps,1) = TEMPOUT(:) ; 219 | PRESIN(1:ntps,1) = PRESIN(:) ; 220 | PRESOUT(1:ntps,1) = PRESOUT(:) ; 221 | SI(1:ntps,1) = SI(:) ; 222 | PO4(1:ntps,1) = PO4(:) ; 223 | NH4(1:ntps,1) = NH4(:) ; 224 | H2S(1:ntps,1) = H2S(:) ; 225 | ePAR1(1:ntps,1) = ePAR1(:) ; 226 | ePAR2(1:ntps,1) = ePAR2(:) ; 227 | eSAL(1:ntps,1) = eSAL(:) ; 228 | eTEMP(1:ntps,1) = eTEMP(:) ; 229 | eSI(1:ntps,1) = eSI(:) ; 230 | ePO4(1:ntps,1) = ePO4(:) ; 231 | eNH4(1:ntps,1) = eNH4(:) ; 232 | eH2S(1:ntps,1) = eH2S(:) ; 233 | r(1:ntps,1) = r(:) ; 234 | pHSCALEIN(1:ntps,1) = pHSCALEIN(:) ; 235 | K1K2CONSTANTS(1:ntps,1) = K1K2CONSTANTS(:) ; 236 | KSO4CONSTANT(1:ntps,1) = KSO4CONSTANT(:) ; 237 | KFCONSTANT(1:ntps,1) = KFCONSTANT(:) ; 238 | BORON(1:ntps,1) = BORON(:) ; 239 | eCAL(1:ntps,1) = eCAL(:) ; 240 | 241 | % Exclude input variables equal to -999 242 | E = (PAR1~=-999 & PAR2~=-999); 243 | 244 | % Default values for epK 245 | if (isempty(epK)) 246 | epK = [0.002, 0.0075, 0.015, 0.01, 0.01, 0.02, 0.02]; 247 | else 248 | % Check validity of epK 249 | if (length(epK) == 1 && epK == 0) 250 | % this means that the caller does not want to account for errors on dissoc. constants 251 | epK = [0.0 0.0 0.0 0.0 0.0 0.0 0.0]; 252 | elseif (length(epK) ~= 7) 253 | error ('invalid parameter epK: ', epK) 254 | end 255 | end 256 | 257 | % Default value for eBt (also check for incorrectly specified values 258 | if (isempty(eBt)) 259 | eBt = 0.02; 260 | elseif ( ~(isscalar(eBt)) ) 261 | error ('invalid parameter eBt (must be scalar): ') 262 | elseif ( isscalar(eBt)) 263 | if (eBt < 0 || eBt > 1) 264 | error ('The "eBt" input argument is the fractional error. It must be between 0 and 1. Default is 0.02 (a 2% error).') 265 | end 266 | end 267 | 268 | % names of dissociation constants 269 | Knames = {'K0','K1','K2','Kb','Kw','Kspa', 'Kspc'}; 270 | 271 | % Convert error on pH to error on [H+] concentration 272 | % in case where first input variable is pH 273 | isH = (E & PAR1TYPE == 3); 274 | 275 | pH = PAR1(isH); 276 | epH = ePAR1(isH); % Error on pH 277 | H = 10.^(-pH); % H+ concentration 278 | r(isH) = -r(isH); % Inverse sign of 'r' if PAR1 is pH 279 | 280 | % dpH = d(-log10[H]) 281 | % = d(- ln[H] / ln[10] ) 282 | % = -(1/ln[10]) * d (ln[H]) 283 | % = -(1/ln[10]) * (dH / H) 284 | % Thus dH = - ln[1O] * [H] dpH 285 | eH = log(10) * (H .* epH); % Removed the minus sign because all errors (sigmas) are positive by definition 286 | eH = eH * 1e9 ; % Convert from mol/kg to nmol/kg (to have same units as partial derivative) 287 | ePAR1(isH) = eH; 288 | 289 | % Same conversion for second variable 290 | isH = (E & PAR2TYPE == 3); 291 | pH = PAR2(isH); 292 | epH = ePAR2(isH); % Error on pH 293 | H = 10.^(-pH); % H+ concentration 294 | r(isH) = -r(isH); % Inverse sign of 'r' if PAR2 is pH 295 | 296 | eH = log(10) * (H .* epH); 297 | eH = eH * 1e9 ; 298 | ePAR2(isH) = eH; 299 | 300 | % Convert CO2, HCO3, or CO3 from fractional error to umol/kg 301 | isC = (E & PAR1TYPE == 6 | PAR1TYPE == 7 | PAR1TYPE == 8); 302 | C = PAR1(isC); % Parameter 1 303 | eCper = ePAR1(isC); % Fractional relative error on CO2, HCO3, or CO3 304 | eCabs = (eCper).*C; % Convert to umol/kg error 305 | ePAR1(isC) = eCabs; 306 | 307 | isC = (E & PAR2TYPE == 6 | PAR2TYPE == 7 | PAR2TYPE == 8); 308 | C = PAR2(isC); % Parameter 2 309 | eCper = ePAR2(isC); % Fractional relative error on CO2, HCO3, or CO3 310 | eCabs = (eCper).*C; % Convert to umol/kg error 311 | ePAR2(isC) = eCabs; 312 | 313 | % Convert calcium error from mmol/kg to mol/kg 314 | eCAL = eCAL.*1e-3; 315 | 316 | % initialise total square error 317 | sq_err = zeros(ntps,1); 318 | 319 | % Contribution of PAR1 to squared standard error 320 | if (any(ePAR1 ~= 0.0)) 321 | % Compute sensitivities (partial derivatives) 322 | [deriv1(E,:),~,~,headers_err,units_err] = derivnum_adjusted_to_v2_0_5 ('PAR1',... 323 | PAR1(E),PAR2(E),PAR1TYPE(E),PAR2TYPE(E),SAL(E),TEMPIN(E),... 324 | TEMPOUT(E),PRESIN(E),PRESOUT(E),SI(E),PO4(E),NH4(E),H2S(E),... 325 | pHSCALEIN(E),K1K2CONSTANTS(E),KSO4CONSTANT(E),KFCONSTANT(E),BORON(E)); 326 | err = bsxfun(@times,deriv1,ePAR1); 327 | sq_err = bsxfun(@plus,err*0., sq_err); 328 | sq_err = sq_err + err .* err; 329 | end 330 | 331 | % Contribution of PAR2 to squared standard error 332 | if (any (ePAR2 ~= 0.0)) 333 | % Compute sensitivities (partial derivatives) 334 | [deriv2(E,:),~,~,headers_err,units_err] = derivnum_adjusted_to_v2_0_5 ('PAR2',... 335 | PAR1(E),PAR2(E),PAR1TYPE(E),PAR2TYPE(E),SAL(E),TEMPIN(E),... 336 | TEMPOUT(E),PRESIN(E),PRESOUT(E),SI(E),PO4(E),NH4(E),H2S(E),... 337 | pHSCALEIN(E),K1K2CONSTANTS(E),KSO4CONSTANT(E),KFCONSTANT(E),BORON(E)); 338 | err = bsxfun(@times,deriv2,ePAR2); 339 | sq_err = bsxfun(@plus,err*0., sq_err); 340 | sq_err = sq_err + err .* err; 341 | end 342 | 343 | % Contribution of covariance of PAR1 and PAR2 to squared standard error 344 | covariance = nan(ntps,1); 345 | if (any (r ~= 0.0) && any (ePAR1 ~= 0.0) && any (ePAR2 ~= 0.0)) 346 | % Compute covariance from correlation coeff. & std deviations 347 | covariance(E) = r(E) .* ePAR1(E) .* ePAR2(E); 348 | % Contribution to squared error 349 | err2 = bsxfun(@times,2 * deriv1 .* deriv2, covariance); 350 | sq_err = sq_err + err2; 351 | end 352 | 353 | % Contribution of Silion (total dissolved inorganic concentration) to squared standard error 354 | % 355 | % Remark : does not compute error where SI = 0 356 | % because computation of sensitivity to SI fails in that case 357 | % 358 | SI_valid = (E & SI ~= 0 & eSI ~= 0); 359 | if (any (SI_valid)) 360 | % Compute sensitivities (partial derivatives) 361 | [deriv, ~, ~, headers_err, units_err] = derivnum_adjusted_to_v2_0_5 ('sil',PAR1(SI_valid),PAR2(SI_valid),PAR1TYPE(SI_valid),PAR2TYPE(SI_valid),... 362 | SAL(SI_valid),TEMPIN(SI_valid),TEMPOUT(SI_valid),PRESIN(SI_valid),PRESOUT(SI_valid),... 363 | SI(SI_valid),PO4(SI_valid),NH4(SI_valid),H2S(SI_valid),pHSCALEIN(SI_valid),K1K2CONSTANTS(SI_valid),KSO4CONSTANT(SI_valid),... 364 | KFCONSTANT(SI_valid),BORON(SI_valid)); 365 | err = bsxfun(@times,deriv,eSI(SI_valid)); 366 | new_size = [ntps size(err,2)]; 367 | sq_err = zeros(new_size) + sq_err; 368 | sq_err(SI_valid,:) = sq_err(SI_valid,:) + err .* err; 369 | end 370 | 371 | % Contribution of Phosphorus (total dissoloved inorganic concentration) to squared standard error 372 | % 373 | % Remark : does not compute error where PO4 = 0 374 | % because computation of sensitivity to PO4 fails in that case 375 | % 376 | PO4_valid = (E & PO4 ~= 0 & ePO4 ~= 0); 377 | if (any (PO4_valid)) 378 | % Compute sensitivities (partial derivatives) 379 | [deriv, ~, ~, headers_err, units_err] = derivnum_adjusted_to_v2_0_5 ('phos',PAR1(PO4_valid),PAR2(PO4_valid),PAR1TYPE(PO4_valid),PAR2TYPE(PO4_valid),... 380 | SAL(PO4_valid),TEMPIN(PO4_valid),TEMPOUT(PO4_valid),PRESIN(PO4_valid),PRESOUT(PO4_valid),... 381 | SI(PO4_valid),PO4(PO4_valid),NH4(PO4_valid),H2S(PO4_valid),pHSCALEIN(PO4_valid),K1K2CONSTANTS(PO4_valid),KSO4CONSTANT(PO4_valid),... 382 | KFCONSTANT(PO4_valid),BORON(PO4_valid)); 383 | err = bsxfun(@times,deriv,ePO4(PO4_valid)); 384 | new_size = [ntps size(err,2)]; 385 | sq_err = zeros(new_size) + sq_err; 386 | sq_err(PO4_valid,:) = sq_err(PO4_valid,:) + err .* err; 387 | end 388 | 389 | % Contribution of Ammonia (total dissolved inorganic concentration) to squared standard error 390 | % 391 | % Remark : does not compute error where NH4 = 0 392 | % because computation of sensitivity to SI fails in that case 393 | % 394 | NH4_valid = (E & NH4 ~= 0 & eNH4 ~= 0); 395 | if (any (NH4_valid)) 396 | % Compute sensitivities (partial derivatives) 397 | [deriv, ~, ~, headers_err, units_err] = derivnum_adjusted_to_v2_0_5 ('amm',PAR1(NH4_valid),PAR2(NH4_valid),PAR1TYPE(NH4_valid),PAR2TYPE(NH4_valid),... 398 | SAL(NH4_valid),TEMPIN(NH4_valid),TEMPOUT(NH4_valid),PRESIN(NH4_valid),PRESOUT(NH4_valid),... 399 | SI(NH4_valid),PO4(NH4_valid),NH4(NH4_valid),H2S(NH4_valid),pHSCALEIN(NH4_valid),K1K2CONSTANTS(NH4_valid),KSO4CONSTANT(NH4_valid),... 400 | KFCONSTANT(NH4_valid),BORON(NH4_valid)); 401 | err = bsxfun(@times,deriv,eNH4(NH4_valid)); 402 | new_size = [ntps size(err,2)]; 403 | sq_err = zeros(new_size) + sq_err; 404 | sq_err(NH4_valid,:) = sq_err(NH4_valid,:) + err .* err; 405 | end 406 | 407 | % Contribution of Hydrogen Sulfide (total dissoloved inorganic concentration) to squared standard error 408 | % 409 | % Remark : does not compute error where H2S = 0 410 | % because computation of sensitivity to PO4 fails in that case 411 | % 412 | H2S_valid = (E & H2S ~= 0 & eH2S ~= 0); 413 | if (any (H2S_valid)) 414 | % Compute sensitivities (partial derivatives) 415 | [deriv, ~, ~, headers_err, units_err] = derivnum_adjusted_to_v2_0_5 ('hyd',PAR1(H2S_valid),PAR2(H2S_valid),PAR1TYPE(H2S_valid),PAR2TYPE(H2S_valid),... 416 | SAL(H2S_valid),TEMPIN(H2S_valid),TEMPOUT(H2S_valid),PRESIN(H2S_valid),PRESOUT(H2S_valid),... 417 | SI(H2S_valid),PO4(H2S_valid),NH4(H2S_valid),H2S(H2S_valid),pHSCALEIN(H2S_valid),K1K2CONSTANTS(H2S_valid),KSO4CONSTANT(H2S_valid),... 418 | KFCONSTANT(H2S_valid),BORON(H2S_valid)); 419 | err = bsxfun(@times,deriv,eH2S(H2S_valid)); 420 | new_size = [ntps size(err,2)]; 421 | sq_err = zeros(new_size) + sq_err; 422 | sq_err(H2S_valid,:) = sq_err(H2S_valid,:) + err .* err; 423 | end 424 | 425 | % Contribution of T (temperature) to squared standard error 426 | if (any (eTEMP ~= 0.0)) 427 | % Compute sensitivities (partial derivatives) 428 | [deriv(E,:),~,~,headers_err,units_err] = derivnum_adjusted_to_v2_0_5 ('T',... 429 | PAR1(E),PAR2(E),PAR1TYPE(E),PAR2TYPE(E),SAL(E),TEMPIN(E),... 430 | TEMPOUT(E),PRESIN(E),PRESOUT(E),SI(E),PO4(E),NH4(E),H2S(E),... 431 | pHSCALEIN(E),K1K2CONSTANTS(E),KSO4CONSTANT(E),KFCONSTANT(E),BORON(E)); 432 | err = bsxfun(@times,deriv,eTEMP); 433 | sq_err = err*0. + sq_err; 434 | sq_err = sq_err + err .* err; 435 | end 436 | 437 | % Contribution of S (salinity) to squared standard error 438 | if (any (eSAL ~= 0.0)) 439 | % Compute sensitivities (partial derivatives) 440 | [deriv(E,:),~,~,headers_err,units_err] = derivnum_adjusted_to_v2_0_5 ('S',... 441 | PAR1(E),PAR2(E),PAR1TYPE(E),PAR2TYPE(E),SAL(E),TEMPIN(E),... 442 | TEMPOUT(E),PRESIN(E),PRESOUT(E),SI(E),PO4(E),NH4(E),H2S(E),... 443 | pHSCALEIN(E),K1K2CONSTANTS(E),KSO4CONSTANT(E),KFCONSTANT(E),BORON(E)); 444 | err = bsxfun(@times,deriv,eSAL); 445 | sq_err = err*0. + sq_err; 446 | sq_err = sq_err + err .* err; 447 | end 448 | 449 | % Calculate dissociation constants 450 | data = CO2SYS_adjusted_to_v2_0_5(PAR1,PAR2,PAR1TYPE,PAR2TYPE,SAL,TEMPIN,TEMPOUT,PRESIN,PRESOUT,SI,PO4,NH4,H2S,pHSCALEIN,K1K2CONSTANTS,KSO4CONSTANT,KFCONSTANT,BORON); 451 | 452 | % Calculate [Ca++] 453 | % ' Riley, J. P. and Tongudai, M., Chemical Geology 2:263-269, 1967: 454 | % ' this is .010285.*Sali./35 455 | Ca = 0.02128./40.087.*(SAL./1.80655);% ' in mol/kg-SW 456 | 457 | % Contribution of all pKi to squared standard error 458 | for i = 1:length(epK) 459 | 460 | % if error on Ki is given 461 | if (epK(i) ~= 0.0) 462 | % Select Ki 463 | switch i 464 | case 1 465 | Ki = data(:,63); % K0 466 | case 2 467 | Ki = data(:,64); % K1 468 | case 3 469 | Ki = data(:,65); % K2 470 | case 4 471 | Ki = data(:,69); % KB 472 | case 5 473 | Ki = data(:,68); % KW 474 | case 6 475 | % Recompute KAr from OmegaAr and ions [Ca++] and [CO3--] concentrations 476 | OmegaAr = data(:,18); 477 | CO3 = data(:,7) * 1e-6; 478 | Ki = CO3.*Ca./OmegaAr; 479 | Ki(isnan(Ki)) = 0; 480 | case 7 481 | % Recompute KCa from OmegaCa and ions [Ca++] and [CO3--] concentrations 482 | OmegaCa = data(:,17); 483 | CO3 = data(:,7) * 1e-6; 484 | Ki = CO3.*Ca./OmegaCa; 485 | Ki(isnan(Ki)) = 0; 486 | end 487 | 488 | % compute error on Ki from that on pKi 489 | eKi = - epK(i) * Ki * log(10); 490 | % Compute sensitivities (partial derivatives) 491 | [deriv(E,:),~,~,headers_err,units_err] = derivnum_adjusted_to_v2_0_5 (cell2mat(Knames(1,i)),... 492 | PAR1(E),PAR2(E),PAR1TYPE(E),PAR2TYPE(E),... 493 | SAL(E),TEMPIN(E),TEMPOUT(E),PRESIN(E),PRESOUT(E),... 494 | SI(E),PO4(E),NH4(E),H2S(E),pHSCALEIN(E),K1K2CONSTANTS(E),... 495 | KSO4CONSTANT(E),KFCONSTANT(E),BORON(E)); 496 | err = bsxfun(@times, deriv, eKi); 497 | sq_err = err*0. + sq_err; 498 | sq_err = sq_err + err .* err; 499 | end 500 | end 501 | 502 | % Contribution of Boron (total dissoloved boron concentration) to squared standard error 503 | if (eBt ~= 0) 504 | % Compute sensitivities (partial derivatives) 505 | [deriv(E,:),~,~,headers_err,units_err] = derivnum_adjusted_to_v2_0_5 ('bor',... 506 | PAR1(E),PAR2(E),PAR1TYPE(E),PAR2TYPE(E),SAL(E),TEMPIN(E),... 507 | TEMPOUT(E),PRESIN(E),PRESOUT(E),SI(E),PO4(E),NH4(E),H2S(E),... 508 | pHSCALEIN(E),K1K2CONSTANTS(E),KSO4CONSTANT(E),KFCONSTANT(E),BORON(E)); 509 | err = bsxfun(@times, deriv, (eBt*data(:,93)*1e-6)); % where TB = data(:,93) in umol B/kg 510 | new_size = [ntps size(err,2)]; 511 | sq_err = zeros(new_size) + sq_err; 512 | sq_err = sq_err + err .* err; 513 | end 514 | 515 | % Contribution of Calcium (total dissoloved calcium concentration) to squared standard error 516 | CAL_valid = (E & eCAL ~= 0); 517 | if (any (CAL_valid)) 518 | % Compute sensitivities (partial derivatives) 519 | [deriv,~,~,headers_err,units_err] = derivnum_adjusted_to_v2_0_5 ('cal',... 520 | PAR1(CAL_valid),PAR2(CAL_valid),PAR1TYPE(CAL_valid),PAR2TYPE(CAL_valid),SAL(CAL_valid),TEMPIN(CAL_valid),... 521 | TEMPOUT(CAL_valid),PRESIN(CAL_valid),PRESOUT(CAL_valid),SI(CAL_valid),PO4(CAL_valid),NH4(CAL_valid),H2S(CAL_valid),... 522 | pHSCALEIN(CAL_valid),K1K2CONSTANTS(CAL_valid),KSO4CONSTANT(CAL_valid),KFCONSTANT(CAL_valid),BORON(CAL_valid)); 523 | err = bsxfun(@times,deriv,eCAL(CAL_valid)); 524 | new_size = [ntps size(err,2)]; 525 | sq_err = zeros(new_size) + sq_err; 526 | sq_err(CAL_valid,:) = sq_err(CAL_valid,:) + err .* err; 527 | end 528 | 529 | % Compute and return resulting total error (or uncertainty) 530 | total_error = sqrt (sq_err); 531 | total_error(isnan(total_error))=-999; 532 | headers = strcat('u(',headers_err,')'); 533 | units = strcat('(',units_err,')'); 534 | end 535 | --------------------------------------------------------------------------------