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:

\[A_{434,blank} = \frac{\text{Ratio434}_{blank}}{16384}\]
\[A_{620,blank} = \frac{\text{Ratio620}_{blank}}{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\)):

\[r_t = \ln\left(\frac{\text{Therm}}{4096 - \text{Therm}} \times 17400\right)\]

For 14-bit hardware (full-scale count \(= 16384\)):

\[r_t = \ln\left(\frac{\text{Therm}}{16384 - \text{Therm}} \times 17400\right)\]

In both cases, temperature is then:

\[T_C = \frac{1}{0.0010183 + 0.000241 \times r_t + 1.5 \times 10^{-7} \times {r_t}^3} - 273.15\]

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:

\[A_{434} = -\log_{10}\left(\frac{\text{Ratio434}_{meas}}{A_{434,blank}}\right)\]
\[A_{620} = -\log_{10}\left(\frac{\text{Ratio620}_{meas}}{A_{620,blank}}\right)\]

The absorbance ratio is:

\[R = \frac{A_{620}}{A_{434}}\]

The SAMI response (\(RCO_2\)) and intermediate temperature-correction terms are:

\[RCO_2 = -\log_{10}\left(\frac{R - e_1}{e_2 - e_3 \times R}\right)\]
\[RCO_2 = RCO_2 + 0.007 \times (T_C - \text{CalT})\]
\[T_{coeff} = 0.0075778 + 0.0012389 \times RCO_2 - 0.00048757 \times {RCO_2}^2\]
\[Tcor\_RCO_2 = RCO_2 + T_{coeff} \times (T_C - \text{CalT})\]

The final pCO\(_2\) is obtained from the quadratic calibration curve:

\[qcc = \frac{-\text{CalB} + \sqrt{\text{CalB}^2 - 4 \times \text{CalA} \times (\text{CalC} - Tcor\_RCO_2)}}{2 \times \text{CalA}}\]
\[pCO_2 = 10^{qcc}\]

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:

\[pCO_2 = \frac{\text{xCO}_2 \times p}{\text{STD}}\]

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.

\[pCO_2,air = pCO_2,air \div 10^6\]
\[pCO_2,water = pCO_2,water \div 10^6\]

Step 2 — Compute the Schmidt number \(S_c\) from sea surface temperature \(t\) (\(^\circ\)C) (Wanninkhof, 1992, Table A1):

\[S_c = 2073.1 - 125.62 \times t + 3.6276 \times t^2 - 0.043219 \times t^3\]

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):

\[k = \frac{0.27 \times {u_{10}}^2 \times \sqrt{660 \div S_c}}{100 \times 3600}\]

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):

\[T100 = T \div 100\]
\[\begin{align} K_0 &= 1000 \times \exp(-58.0931 + 90.5069 \times (100 / T) + 22.2940 \times \ln(T100) \\ &\quad + s \times (0.027766 - 0.025888 \times T100 + 0.0050578 \times T100^2)) \end{align}\]

where \(s\) is the sea surface salinity.

Step 5 — Compute the flux (Wanninkhof, 1992, Eqn. A2):

\[F = k \times K_0 \times (pCO_2,water - pCO_2,air)\]

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:
  • light ((array_like, shape(N, M))) –

    Array of raw light measurements from the SAMI2-CO2 instrument. Column 6 is Ratio434 and column 7 is Ratio620.

  • therm (array_like) –

    PCO2W thermistor temperature (CO2THRM_L1) [degC].

  • ea434 (array_like) –

    Calibration coefficient 1.

  • eb434 (array_like) –

    Calibration coefficient 2.

  • ea620 (array_like) –

    Calibration coefficient 3.

  • eb620 (array_like) –

    Calibration coefficient 4.

  • calt (array_like) –

    Calibration coefficient 5 (temperature reference).

  • cala (array_like) –

    Calibration coefficient 6.

  • calb (array_like) –

    Calibration coefficient 7.

  • calc (array_like) –

    Calibration coefficient 8.

  • a434blank (array_like) –

    Normalized blank at 434 nm (from pco2_blank) [unitless].

  • a620blank (array_like) –

    Normalized blank at 620 nm (from pco2_blank) [unitless].

Returns:
  • pco2( ndarray ) –

    Partial pressure of CO2 in seawater (PCO2WAT_L1) [uatm]. Blank records (where Ratio434 == Ratio620 after blank correction) are set to the system fill value.

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
def 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
    ----------
    light : array_like, shape (N, M)
        Array of raw light measurements from the SAMI2-CO2 instrument.
        Column 6 is Ratio434 and column 7 is Ratio620.
    therm : array_like
        PCO2W thermistor temperature (CO2THRM_L1) [degC].
    ea434 : array_like
        Calibration coefficient 1.
    eb434 : array_like
        Calibration coefficient 2.
    ea620 : array_like
        Calibration coefficient 3.
    eb620 : array_like
        Calibration coefficient 4.
    calt : array_like
        Calibration coefficient 5 (temperature reference).
    cala : array_like
        Calibration coefficient 6.
    calb : array_like
        Calibration coefficient 7.
    calc : array_like
        Calibration coefficient 8.
    a434blank : array_like
        Normalized blank at 434 nm (from pco2_blank) [unitless].
    a620blank : array_like
        Normalized blank at 620 nm (from pco2_blank) [unitless].

    Returns
    -------
    pco2 : ndarray
        Partial pressure of CO2 in seawater (PCO2WAT_L1) [uatm].
        Blank records (where Ratio434 == Ratio620 after blank
        correction) are set to the system fill value.

    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.
    """
    # Fixed equilibration constants provided by Sunburst Sensors
    e1 = 0.0043
    e2 = 2.136
    e3 = 0.2105

    # Extract 434 nm and 620 nm ratios from light array
    ratio434 = light[:, 6]
    ratio620 = light[:, 7]

    # Blank-correct the absorbance ratios
    ar434 = (ratio434 / a434blank)
    ar4620 = (ratio620 / a620blank)

    # Map blank records (ar434 == ar4620) to avoid log domain errors;
    # these will be set to fill_value at the end
    m = np.where(ar434 == ar4620)[0]
    ar434[m] = 0.99999
    ar4620[m] = 0.99999

    # Compute blank-corrected absorbances and their ratio
    a434 = -1 * np.log10(ar434)
    a620 = -1 * np.log10(ar4620)
    ratio = a620 / a434

    # Compute pCO2 via temperature-corrected SAMI response
    v1 = ratio - e1
    v2 = e2 - e3 * ratio
    rco21 = -1 * np.log10(v1 / v2)
    rco22 = (therm - calt) * 0.007 + rco21
    t_coeff = 0.0075778 + 0.0012389 * rco22 - 0.00048757 * rco22**2
    t_cor_rco2 = rco21 + t_coeff * (therm - calt)
    pco2 = 10.**((-1. * calb + (calb**2 - (4. * cala * (calc - t_cor_rco2)))**0.5) / (2. * cala))
    pco2[m] = fill_value

    return np.real(pco2)

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:
  • xco2 (array_like) –

    CO2 mole fraction in air or surface seawater (XCO2ATM_L0 or XCO2SSW_L0) [ppm].

  • p (array_like) –

    Gas stream pressure (PRESAIR_L0) [mbar].

  • std (float, default: 1013.25 ) –

    Standard atmospheric pressure [mbar/atm]. Default is 1013.25.

Returns:
  • ppres( ndarray ) –

    Partial pressure of CO2 in air or surface seawater (PCO2ATM_L1 or PCO2SSW_L1) [uatm].

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
def 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
    ----------
    xco2 : array_like
        CO2 mole fraction in air or surface seawater (XCO2ATM_L0 or
        XCO2SSW_L0) [ppm].
    p : array_like
        Gas stream pressure (PRESAIR_L0) [mbar].
    std : float, optional
        Standard atmospheric pressure [mbar/atm]. Default is 1013.25.

    Returns
    -------
    ppres : ndarray
        Partial pressure of CO2 in air or surface seawater
        (PCO2ATM_L1 or PCO2SSW_L1) [uatm].

    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.
    """
    ppres = xco2 * p / std
    return ppres

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:
  • pco2w (array_like) –

    Partial pressure of CO2 in seawater (PCO2SSW_L1) [uatm].

  • pco2a (array_like) –

    Partial pressure of CO2 in air (PCO2ATM_L1) [uatm].

  • u10 (array_like) –

    Normalized wind speed at 10 m height (WIND10M_L2) [m s^-1].

  • t (array_like) –

    Sea surface temperature (TEMPSRF_L1) [degC].

  • s (array_like) –

    Sea surface salinity (SALSURF_L2) [psu].

Returns:
  • flux( ndarray ) –

    Estimated flux of CO2 from the ocean to the atmosphere (CO2FLUX_L2) [mol m^-2 s^-1]. Positive values indicate flux from the ocean into the atmosphere.

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
def 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
    ----------
    pco2w : array_like
        Partial pressure of CO2 in seawater (PCO2SSW_L1) [uatm].
    pco2a : array_like
        Partial pressure of CO2 in air (PCO2ATM_L1) [uatm].
    u10 : array_like
        Normalized wind speed at 10 m height (WIND10M_L2) [m s^-1].
    t : array_like
        Sea surface temperature (TEMPSRF_L1) [degC].
    s : array_like
        Sea surface salinity (SALSURF_L2) [psu].

    Returns
    -------
    flux : ndarray
        Estimated flux of CO2 from the ocean to the atmosphere
        (CO2FLUX_L2) [mol m^-2 s^-1]. Positive values indicate flux
        from the ocean into the atmosphere.

    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).
    """
    # Convert uatm to atm
    pco2a = pco2a / 1.0e6
    pco2w = pco2w / 1.0e6

    # Schmidt number (Wanninkhof, 1992, Table A1)
    Sc = 2073.1 - (125.62 * t) + (3.6276 * t**2) - (0.043219 * t**3)

    # Gas transfer velocity in cm h^-1 (Sweeney et al., 2007, Fig. 3 and Table 1),
    # converted to m s^-1
    k = 0.27 * u10**2 * np.sqrt(660.0 / Sc)
    k = k / (100.0 * 3600.0)

    # Absolute temperature
    T = t + 273.15

    # CO2 solubility, volume version, mol atm^-1 m^-3
    # (Weiss, 1974, Eqn. 12 and Table I)
    T100 = T / 100
    K0 = 1000 * np.exp(-58.0931 + (90.5069 * (100 / T)) + (22.2940 * np.log(T100)) +
                       s * (0.027766 - (0.025888 * T100) + (0.0050578 * T100**2)))

    # Air-sea CO2 flux (Wanninkhof, 1992, Eqn. A2)
    flux = k * K0 * (pco2w - pco2a)
    return flux

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:
  • light ((array_like, shape(N, M))) –

    Array of raw light measurements from the SAMI2-CO2 instrument. Column index 6 contains the 434 nm ratio counts (Ratio434).

Returns:
  • a434ratio( (ndarray, shape(N)) ) –

    Optical absorbance ratio at 434 nm (CO2ABS1_L0) [unitless].

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
def pco2_abs434_ratio(light):
    """
    Extract the L0 absorbance ratio at 434 nm from a PCO2W light array.

    Parameters
    ----------
    light : array_like, shape (N, M)
        Array of raw light measurements from the SAMI2-CO2 instrument.
        Column index 6 contains the 434 nm ratio counts (Ratio434).

    Returns
    -------
    a434ratio : ndarray, shape (N,)
        Optical absorbance ratio at 434 nm (CO2ABS1_L0) [unitless].

    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.
    """
    light = np.atleast_2d(light)
    a434ratio = light[:, 6]
    return a434ratio

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:
  • light ((array_like, shape(N, M))) –

    Array of raw light measurements from the SAMI2-CO2 instrument. Column index 7 contains the 620 nm ratio counts (Ratio620).

Returns:
  • a620ratio( (ndarray, shape(N)) ) –

    Optical absorbance ratio at 620 nm (CO2ABS2_L0) [unitless].

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
def pco2_abs620_ratio(light):
    """
    Extract the L0 absorbance ratio at 620 nm from a PCO2W light array.

    Parameters
    ----------
    light : array_like, shape (N, M)
        Array of raw light measurements from the SAMI2-CO2 instrument.
        Column index 7 contains the 620 nm ratio counts (Ratio620).

    Returns
    -------
    a620ratio : ndarray, shape (N,)
        Optical absorbance ratio at 620 nm (CO2ABS2_L0) [unitless].

    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.
    """
    light = np.atleast_2d(light)
    a620ratio = light[:, 7]
    return a620ratio

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:
  • raw_blank (array_like) –

    Raw optical absorbance blank counts at 434 or 620 nm [counts].

Returns:
  • blank( ndarray ) –

    Normalized optical absorbance blank at 434 or 620 nm [unitless].

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
def pco2_blank(raw_blank):
    """
    Convert a raw PCO2W blank count to a normalized absorbance blank.

    Parameters
    ----------
    raw_blank : array_like
        Raw optical absorbance blank counts at 434 or 620 nm [counts].

    Returns
    -------
    blank : ndarray
        Normalized optical absorbance blank at 434 or 620 nm [unitless].

    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.
    """
    blank = raw_blank / 16384.
    return blank

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:
  • traw (array_like) –

    Raw thermistor counts (CO2THRM_L0) [counts].

  • sami_bits (int, default: 12 ) –

    ADC resolution of the SAMI hardware. Use 12 for original hardware and 14 for upgraded hardware. Default is 12.

Returns:
  • therm( ndarray ) –

    Thermistor temperature [degC].

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
def pco2_thermistor(traw, sami_bits=12):
    """
    Convert raw PCO2W thermistor counts to temperature in degrees C.

    Parameters
    ----------
    traw : array_like
        Raw thermistor counts (CO2THRM_L0) [counts].
    sami_bits : int, optional
        ADC resolution of the SAMI hardware. Use 12 for original
        hardware and 14 for upgraded hardware. Default is 12.

    Returns
    -------
    therm : ndarray
        Thermistor temperature [degC].

    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.
    """
    traw = np.atleast_1d(traw)
    sami_bits = np.atleast_1d(sami_bits)

    # Conversion depends on whether the SAMI is 12-bit or 14-bit hardware
    if sami_bits[0] == 14:
        rt = np.log((traw / (16384.0 - traw)) * 17400.0)
    else:
        rt = np.log((traw / (4096. - traw)) * 17400.)
    inv_t = 0.0010183 + 0.000241 * rt + 0.00000015 * rt**3
    therm = (1. / inv_t) - 273.15
    return therm

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:
  • braw (array_like) –

    Raw battery voltage counts [counts].

  • sami_bits (int) –

    ADC resolution of the SAMI hardware. Use 12 for original hardware and 14 for upgraded hardware.

Returns:
  • volts( ndarray ) –

    Battery voltage [V].

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
def pco2_battery(braw, sami_bits):
    """
    Convert raw PCO2W battery counts to battery voltage in Volts.

    Parameters
    ----------
    braw : array_like
        Raw battery voltage counts [counts].
    sami_bits : int
        ADC resolution of the SAMI hardware. Use 12 for original
        hardware and 14 for upgraded hardware.

    Returns
    -------
    volts : ndarray
        Battery voltage [V].

    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.
    """
    braw = np.atleast_1d(braw)
    sami_bits = np.atleast_1d(sami_bits)

    if sami_bits[0] == 14:
        volts = braw * 3. / 4000.
    else:
        volts = braw * 15. / 4096.
    return volts

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:
  • mtype (array_like) –

    Measurement type: 4 for pCO2 measurement, 5 for blank [unitless].

  • light ((array_like, shape(N, M))) –

    Array of raw light measurements from the SAMI2-CO2 instrument.

  • therm (array_like) –

    PCO2W thermistor temperature (CO2THRM_L1) [degC].

  • ea434 (array_like) –

    Calibration coefficient 1.

  • eb434 (array_like) –

    Calibration coefficient 2.

  • ea620 (array_like) –

    Calibration coefficient 3.

  • eb620 (array_like) –

    Calibration coefficient 4.

  • calt (array_like) –

    Calibration coefficient 5 (temperature reference).

  • cala (array_like) –

    Calibration coefficient 6.

  • calb (array_like) –

    Calibration coefficient 7.

  • calc (array_like) –

    Calibration coefficient 8.

  • a434blank (array_like) –

    Blank measurement at 434 nm (CO2ABS1_L0) [counts].

  • a620blank (array_like) –

    Blank measurement at 620 nm (CO2ABS2_L0) [counts].

Returns:
  • pco2( ndarray ) –

    Partial pressure of CO2 in seawater (PCO2WAT_L1) [uatm]. Blank records (mtype == 5) are set to the system fill value.

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
def 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
    ----------
    mtype : array_like
        Measurement type: 4 for pCO2 measurement, 5 for blank
        [unitless].
    light : array_like, shape (N, M)
        Array of raw light measurements from the SAMI2-CO2 instrument.
    therm : array_like
        PCO2W thermistor temperature (CO2THRM_L1) [degC].
    ea434 : array_like
        Calibration coefficient 1.
    eb434 : array_like
        Calibration coefficient 2.
    ea620 : array_like
        Calibration coefficient 3.
    eb620 : array_like
        Calibration coefficient 4.
    calt : array_like
        Calibration coefficient 5 (temperature reference).
    cala : array_like
        Calibration coefficient 6.
    calb : array_like
        Calibration coefficient 7.
    calc : array_like
        Calibration coefficient 8.
    a434blank : array_like
        Blank measurement at 434 nm (CO2ABS1_L0) [counts].
    a620blank : array_like
        Blank measurement at 620 nm (CO2ABS2_L0) [counts].

    Returns
    -------
    pco2 : ndarray
        Partial pressure of CO2 in seawater (PCO2WAT_L1) [uatm].
        Blank records (mtype == 5) are set to the system fill value.

    See Also
    --------
    pco2_calc_pco2 : Core algorithm; use directly for all-record access.
    """
    mtype = np.atleast_1d(mtype)
    light = np.atleast_2d(light)
    therm = np.atleast_1d(therm)
    ea434 = np.atleast_1d(ea434)
    eb434 = np.atleast_1d(eb434)
    ea620 = np.atleast_1d(ea620)
    eb620 = np.atleast_1d(eb620)
    calt = np.atleast_1d(calt)
    cala = np.atleast_1d(cala)
    calb = np.atleast_1d(calb)
    calc = np.atleast_1d(calc)
    a434blank = np.atleast_1d(a434blank)
    a620blank = np.atleast_1d(a620blank)

    pco2 = pco2_calc_pco2(light, therm, ea434, eb434, ea620, eb620,
                          calt, cala, calb, calc, a434blank, a620blank)

    # Reset blank measurement records to the fill value
    m = np.where(mtype == 5)[0]
    pco2[m] = fill_value

    return pco2

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.

OOI (2012). Data Product Specification for Partial Pressure of CO2 in Air and Surface Seawater. Document Control Number 1341-00260.

OOI (2018). Data Product Specification for Partial Pressure of CO2 in Seawater. Document Control Number 1341-00490.

OOI (2012). Data Product Specification for Flux of CO2 from the Ocean into the Atmosphere. Document Control Number 1341-00270.