├── .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 |
[](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 |
--------------------------------------------------------------------------------