CO2 Functions
Background
The Ocean Observatories Initiative deploys two families of CO\(_2\) instruments across its surface and subsurface platforms. The table below lists the OOI instrument classes covered by this module.
| Class | Hardware | Platform | Designator meaning |
|---|---|---|---|
| PCO2W | Sunburst SAMI-CO2 | Moored (fixed depth) and Profiling | pCO\(_2\), Seawater |
| PCO2A | Pro-Oceanus CO2-Pro | Surface buoys | pCO\(_2\), Air-Sea |
co2_functions.py processes data from both instrument classes and computes
a third derived product, CO\(_2\) flux from the ocean to the atmosphere
(CO2FLUX_L2), using auxiliary bulk meteorology data from the METBK instrument
class.
PCO2WAT_L1 — Partial Pressure of CO2 in Seawater
PCO2WAT_L1 is the partial pressure of CO\(_2\) (pCO\(_2\), \(\mu\)atm) in seawater at depth, computed from the Sunburst SAMI-CO2 (PCO2W). The SAMI-CO2 uses a bromothymol blue (BTB) pH indicator solution that equilibrates with the ambient seawater through a permeable silicone membrane. The equilibrated indicator solution passes through an optical cell where absorbance is measured at 434 nm and 620 nm — the peak absorbance wavelengths for the protonated and deprotonated forms of the indicator. Periodic blank measurements (every 3.5 days) correct for drift in the electro-optical system.
L0 Inputs and Blank Normalization
The SAMI-CO2 outputs two record types:
- Type 4 — regular measurement (used to compute PCO2WAT_L1)
- Type 5 — blank measurement (used to correct Type 4 records)
Each record contains a 434 nm ratio (Ratio434), a 620 nm ratio (Ratio620),
and a raw thermistor count (Therm). Raw blank counts from Type 5 records are
normalized to dimensionless ratios by dividing by 16384:
Thermistor Temperature
The raw thermistor count is converted to degrees C. The conversion depends on the SAMI hardware generation (12-bit or 14-bit ADC). For 12-bit hardware (full-scale count \(= 4096\)):
For 14-bit hardware (full-scale count \(= 16384\)):
In both cases, temperature is then:
PCO2WAT_L1 Calculation
The L1 pCO\(_2\) is computed from the blank-corrected absorbances and a temperature-corrected SAMI response. The blank-corrected absorbances at each wavelength are:
The absorbance ratio is:
The SAMI response (\(RCO_2\)) and intermediate temperature-correction terms are:
The final pCO\(_2\) is obtained from the quadratic calibration curve:
The equilibration constants \(e_1 = 0.0043\), \(e_2 = 2.136\), \(e_3 = 0.2105\) are fixed values provided by Sunburst Sensors based on laboratory determinations with the BTB indicator batch and are not recalculated per calibration cycle.
The instrument-specific calibration coefficients CalT, CalA, CalB, and CalC are provided by the manufacturer after each calibration or refurbishment cycle and are made available to the OOI Asset Management database via CSV files uploaded to GitHub.
Algorithm note: The Python implementation corrects errors present in the vendor-supplied Matlab example code distributed with DPS 1341-00490 (Appendix A, 2018).
Output accuracy: \(\pm 3 \times \mu\text{atm}\); precision \(<1 \times \mu\text{atm}\) (DPS 1341-00490, Appendix B).
PCO2ATM_L1 and PCO2SSW_L1 — pCO2 in Air and Surface Seawater
PCO2ATM_L1 and PCO2SSW_L1 are the partial pressures of CO\(_2\) (pCO\(_2\), \(\mu\)atm) in atmosphere and surface seawater, respectively. Both are computed from the Pro-Oceanus CO2-Pro Atmosphere (PCO2A) instrument, which determines the CO\(_2\) mole fraction (xCO\(_2\), in ppm) internally by measuring the infrared absorbance of CO\(_2\) and compensating for pressure, temperature, and humidity using onboard firmware. The instrument alternates between air sampling (producing XCO2ATM_L0) and water sampling (producing XCO2SSW_L0).
The xCO\(_2\) mole fractions and the internal gas stream pressure (PRESAIR_L0) are converted to pCO\(_2\) (\(\mu\)atm) using:
where \(p\) is PRESAIR_L0 in mbar and \(\text{STD} = 1013.25\) mbar/atm, the standard atmospheric pressure. Because xCO\(_2\) is in ppm (\(10^{-6}\)), the result is directly in \(\mu\)atm without further scaling.
The same function pco2_ppressure computes both PCO2ATM_L1 and PCO2SSW_L1;
the distinction between air and surface seawater measurements is determined
by the L0 input supplied (XCO2ATM or XCO2SSW) and the record type flag in
the serial data stream.
Output accuracy: \(\pm 1 \times \mu\text{atm}\); precision \(\pm 0.01 \times \mu\text{atm}\) (DPS 1341-00260, Appendix B).
CO2FLUX_L2 — Air-Sea Flux of CO2
CO2FLUX_L2 is the estimate of the flux of CO\(_2\) across the air-sea interface. It is computed from PCO2SSW_L1 and PCO2ATM_L1 and select L1/L2 bulk meteorology inputs (from the METBK): sea surface temperature (TEMPSRF_L1), sea surface salinity (SALSURF_L2), and wind speed at 10 m height (WIND10M_L2).
The flux is positive when directed from the ocean to the atmosphere. The computation follows these steps:
Step 1 — Convert pCO\(_2\) (for both air and water) from \(\mu\)atm to atm.
Step 2 — Compute the Schmidt number \(S_c\) from sea surface temperature \(t\) (\(^\circ\)C) (Wanninkhof, 1992, Table A1):
Step 3 — Compute the gas transfer velocity \(k\) in cm h\(^{-1}\) and convert to m s\(^{-1}\) (Sweeney et al., 2007, Fig. 3 and Table 1):
where \(u_{10}\) is the 10 m wind speed in m s\(^{-1}\).
Step 4 — Compute absolute temperature \(T = t + 273.15\) (K), then compute CO\(_2\) solubility \(K_0\) in mol atm\(^{-1}\) m\(^{-3}\) using the volume formulation (Weiss, 1974, Eqn. 12 and Table I):
where \(s\) is the sea surface salinity.
Step 5 — Compute the flux (Wanninkhof, 1992, Eqn. A2):
The inherent uncertainty of the flux estimate is approximately 10% (DPS 1341-00270, Appendix B).
Core Functions
pco2_calc_pco2(light, therm, ea434, eb434, ea620, eb620, calt, cala, calb, calc, a434blank, a620blank)
Compute PCO2WAT_L1 from the Sunburst SAMI2-CO2 (PCO2W).
Calculates the OOI Level 1 Partial Pressure of CO2 in Seawater (PCO2WAT_L1) from raw light measurements, thermistor temperature, blank corrections, and factory calibration coefficients.
| Parameters: |
|
|---|
| Returns: |
|
|---|
Notes
The e constants (e1=0.0043, e2=2.136, e3=0.2105) are fixed values provided by Sunburst Sensors based on lab determinations with the bromothymol blue (BTB) indicator solution. They are not recalculated per calibration cycle. The final pCO2 is computed from a quadratic calibration curve using coefficients CalA, CalB, and CalC (cala, calb, calc). All calibration coefficients are from factory calibration sheets.
The Python implementation corrects errors present in the vendor- supplied Matlab code (DPS Appendix A, 2018). The corrected algorithm was verified against the vendor and is authoritative.
Source code in ion_functions/data/co2_functions.py
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 | |
Additional Notes
The ea434, eb434, ea620, and eb620 calibration coefficient
parameters are accepted as inputs for backward compatibility but are
not used in the calculation. The original formulation derived \(e_1\),
\(e_2\), and \(e_3\) from these per-instrument values; the current
algorithm uses fixed constants supplied by Sunburst Sensors
(\(e_1 = 0.0043\), \(e_2 = 2.136\), \(e_3 = 0.2105\)) for all instruments.
The retained parameters and the commented-out original formulation in
the code document this evolution.
History
| Date | Author | Change |
|---|---|---|
| unknown | J. Newton (Sunburst Sensors, LLC) | Original Matlab code |
| 2013-04-20 | Christopher Wingard | Initial Python code |
| 2014-02-19 | Christopher Wingard | Updated comments |
| 2014-03-19 | Christopher Wingard | Optimized |
| 2018-03-04 | Christopher Wingard | Corrected blank normalization and temperature correction per vendor-revised algorithm |
| 2023-01-12 | Mark Steiner | Changed therm argument to degrees C input |
| 2023-08-15 | Samuel Dahlberg | Renamed local variables to follow naming convention |
| 2025-04-20 | Christopher Wingard | Converted to NumPy docstring format; updated documentation |
pco2_ppressure(xco2, p, std=1013.25)
Compute PCO2ATM_L1 or PCO2SSW_L1 from the Pro-Oceanus CO2-Pro.
Converts the L0 CO2 mole fraction (XCO2ATM or XCO2SSW) and L0 gas stream pressure (PRESAIR) into the L1 partial pressure of CO2 in air (PCO2ATM_L1) or surface seawater (PCO2SSW_L1).
| Parameters: |
|
|---|
| Returns: |
|
|---|
Notes
Because xco2 is in ppm (10^-6), the result of xco2 * p / std is directly in uatm without further scaling. The instrument computes xco2 internally with pressure, temperature, and humidity compensation applied by onboard firmware. All temperature, humidity, and pressure compensation is performed onboard; ion-functions applies only the final unit conversion. Instrument results are valid between 0 and 35 degC.
Source code in ion_functions/data/co2_functions.py
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 | |
History
| Date | Author | Change |
|---|---|---|
| 2014-10-27 | Christopher Wingard | Initial Python code |
| 2023-08-15 | Samuel Dahlberg | Removed use of numexpr |
| 2025-04-20 | Christopher Wingard | Converted to NumPy docstring format; updated documentation |
pco2_co2flux(pco2w, pco2a, u10, t, s)
Compute CO2FLUX_L2, the air-sea flux of CO2.
Estimates the OOI Level 2 flux of CO2 from the ocean to the atmosphere using L1 partial pressures of CO2 in seawater and air, and L1/L2 bulk meteorology inputs.
| Parameters: |
|
|---|
| Returns: |
|
|---|
Notes
Follows the bulk formula of Wanninkhof (1992) with the gas transfer velocity parameterization of Sweeney et al. (2007) and the CO2 solubility of Weiss (1974). pco2w and pco2a are converted from uatm to atm before computing the flux. The Schmidt number polynomial uses Wanninkhof (1992) Table A1 coefficients. Gas transfer velocity k is in cm h^-1 from Sweeney et al. (2007) and converted to m s^-1. The volume-based solubility formulation of Weiss (1974) is used (mol atm^-1 m^-3). An inherent uncertainty of approximately 10% is expected in the flux estimate (Wanninkhof, 1992).
Source code in ion_functions/data/co2_functions.py
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 | |
History
| Date | Author | Change |
|---|---|---|
| 2012-03-28 | Matthias Lankhorst | Original Matlab code |
| 2013-04-20 | Christopher Wingard | Initial Python code |
| 2025-04-20 | Christopher Wingard | Converted to NumPy docstring format; updated documentation |
Helper Functions
pco2_abs434_ratio(light)
Extract the L0 absorbance ratio at 434 nm from a PCO2W light array.
| Parameters: |
|
|---|
| Returns: |
|
|---|
Notes
Extracts column index 6 from the instrument light measurement array. This L0 value is used by pco2_blank and pco2_calc_pco2 to compute the blank-corrected absorbance at 434 nm.
Source code in ion_functions/data/co2_functions.py
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | |
History
| Date | Author | Change |
|---|---|---|
| 2013-04-20 | Christopher Wingard | Initial code |
| 2014-02-19 | Christopher Wingard | Updated comments |
| 2025-04-20 | Christopher Wingard | Converted to NumPy docstring format; updated documentation |
pco2_abs620_ratio(light)
Extract the L0 absorbance ratio at 620 nm from a PCO2W light array.
| Parameters: |
|
|---|
| Returns: |
|
|---|
Notes
Extracts column index 7 from the instrument light measurement array. This L0 value is used by pco2_blank and pco2_calc_pco2 to compute the blank-corrected absorbance at 620 nm.
Source code in ion_functions/data/co2_functions.py
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | |
History
| Date | Author | Change |
|---|---|---|
| 2013-04-20 | Christopher Wingard | Initial code |
| 2014-02-19 | Christopher Wingard | Updated comments |
| 2025-04-20 | Christopher Wingard | Converted to NumPy docstring format; updated documentation |
pco2_blank(raw_blank)
Convert a raw PCO2W blank count to a normalized absorbance blank.
| Parameters: |
|
|---|
| Returns: |
|
|---|
Notes
Divides raw blank counts by 16384 (2^14) to normalize to a dimensionless ratio consistent with the blank-correction step in pco2_calc_pco2. This normalization reflects the vendor-corrected algorithm as of 2018.
Source code in ion_functions/data/co2_functions.py
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | |
History
| Date | Author | Change |
|---|---|---|
| 2013-04-20 | Christopher Wingard | Initial code |
| 2014-02-19 | Christopher Wingard | Updated comments |
| 2014-02-28 | Christopher Wingard | Updated to accept raw blank values from a sparse array |
| 2018-03-04 | Christopher Wingard | Updated blank normalization per vendor-corrected algorithm |
| 2025-04-20 | Christopher Wingard | Converted to NumPy docstring format; updated documentation |
pco2_thermistor(traw, sami_bits=12)
Convert raw PCO2W thermistor counts to temperature in degrees C.
| Parameters: |
|
|---|
| Returns: |
|
|---|
Notes
The conversion differs by hardware generation. For 12-bit hardware the full-scale count is 4096; for 14-bit hardware it is 16384. In both cases the thermistor resistance ratio is multiplied by 17400 before taking the natural log. The log value is used in a three-term inverse-temperature polynomial to obtain temperature in Kelvin, which is converted to degrees C by subtracting 273.15.
Source code in ion_functions/data/co2_functions.py
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | |
History
| Date | Author | Change |
|---|---|---|
| 2013-04-20 | Christopher Wingard | Initial code |
| 2014-02-19 | Christopher Wingard | Updated comments |
| 2023-01-12 | Mark Steiner | Added sami_bits argument to handle 14-bit hardware |
| 2023-08-15 | Samuel Dahlberg | Renamed local variables; replaced numexpr with numpy |
| 2025-04-20 | Christopher Wingard | Converted to NumPy docstring format; updated documentation |
pco2_battery(braw, sami_bits)
Convert raw PCO2W battery counts to battery voltage in Volts.
| Parameters: |
|
|---|
| Returns: |
|
|---|
Notes
The scaling differs by hardware generation. For 14-bit hardware the full-scale range is 3 V over 4000 counts. For 12-bit hardware the full-scale range is 15 V over 4096 counts.
Source code in ion_functions/data/co2_functions.py
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | |
History
| Date | Author | Change |
|---|---|---|
| 2023-02-23 | Mark Steiner | Initial code |
| 2025-04-20 | Christopher Wingard | Converted to NumPy docstring format; updated documentation |
Wrapper Functions
pco2_pco2wat is the OOI single-output wrapper for the PCO2WAT_L1 data
product. It calls pco2_calc_pco2 for all records and then sets blank
measurement records (mtype == 5) to the system fill value, satisfying the
OOI single-output data product requirement. External users should call
pco2_calc_pco2 directly.
pco2_pco2wat(mtype, light, therm, ea434, eb434, ea620, eb620, calt, cala, calb, calc, a434blank, a620blank)
OOI wrapper for PCO2WAT_L1; returns fill value for blank records.
Calls pco2_calc_pco2 for all records, then replaces results for blank measurement records (mtype == 5) with the fill value.
| Parameters: |
|
|---|
| Returns: |
|
|---|
See Also
pco2_calc_pco2 : Core algorithm; use directly for all-record access.
Source code in ion_functions/data/co2_functions.py
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | |
History
| Date | Author | Change |
|---|---|---|
| 2013-04-20 | Christopher Wingard | Initial code |
| 2014-02-19 | Christopher Wingard | Updated comments |
| 2014-03-19 | Christopher Wingard | Optimized |
| 2017-04-04 | Pete Cable | Updated to use thermistor/blank counts per DPS |
| 2025-04-20 | Christopher Wingard | Converted to NumPy docstring format; updated documentation |
References
DeGrandpre, M. D., Baehr, M. M., and Hammar, T. R. (1999). Calibration-free optical chemical sensors. Analytical Chemistry, 71(6), 1152-1159.
Sweeney, C., Gloor, E., Jacobson, A. R., Key, R. M., McKinley, G., Sarmiento, J. L., and Wanninkhof, R. (2007). Constraining global air-sea gas exchange for CO2 with recent bomb 14C measurements. Global Biogeochemical Cycles, 21, GB2015.
Wanninkhof, R. (1992). Relationship between wind speed and gas exchange over the ocean. Journal of Geophysical Research, 97(C5), 7373-7382.
Weiss, R. F. (1974). Carbon dioxide in water and seawater: the solubility of a non-ideal gas. Marine Chemistry, 2, 203-215.