Skip to content

CTD

Bases: BaseInstrument

Class for processing CTD (conductivity/temperature/depth) data.

Contains methods for loading data from source files, preprocessing, and calculating thermodynamic quantities from CTD observations.

The core functionality is a very limited port of the Gibbs SeaWater (GSW) Oceanographic Toolbox (TEOS-10, https://www.teos-10.org). Only the equation of state and directly derived quantities are implemented. Variable names generally follow the GSW conventions for consistency with the source code.

Burst dictionary conventions

Variables in a burst dict are assumed to be 2-D arrays of shape (n_heights, n_samples), where the first axis corresponds to instrument depths (length self.n_heights) and the second axis is time. The individual thermodynamic methods accept any Numeric type and broadcast over these arrays without modification.

Standard burst dict input keys recognized by CTD.derive:

sp  : practical salinity (PSS-78)                         (unitless)
t   : in-situ temperature                                    (deg C)
p   : sea pressure (absolute pressure - 10.1325 dbar)         (dbar)
lat : latitude                                   (deg N) -- optional

Output keys added by CTD.derive:

sa          : Absolute Salinity                               (g/kg)
ct          : Conservative Temperature                       (deg C)
rho         : in-situ density                               (kg/m^3)
sigma0      : potential density anomaly ref 0 dbar           (kg/m^3)
alpha       : thermal expansion coefficient                    (1/K)
beta        : haline contraction coefficient                  (kg/g)
sound_speed : speed of sound                                   (m/s)
t_freezing  : in-situ freezing temperature                   (deg C)
cp          : isobaric heat capacity                       (J/(kg K))
nu          : kinematic viscosity                            (m^2/s)
N2          : buoyancy frequency squared (n_heights > 1)     (1/s^2)
z           : depth (positive downward)                          (m)
References

IOC, SCOR and IAPSO, 2010: The international thermodynamic equation of seawater - 2010. Intergovernmental Oceanographic Commission, Manuals and Guides No. 56, UNESCO.

Roquet, F., G. Madec, T.J. McDougall, P.M. Barker, 2015: Accurate polynomial expressions for the density and specific volume of seawater using the TEOS-10 standard. Ocean Modelling, 90, 29-43.

McDougall, T.J. and P.M. Barker, 2011: Getting started with TEOS-10 and the Gibbs Seawater (GSW) Oceanographic Toolbox. SCOR/IAPSO WG 127(532), 1-28.

Source code in src/pytoast/ocean/ctd.py
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 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
132
133
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
165
166
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
237
238
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
332
333
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
370
371
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
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
class CTD(BaseInstrument):
    """Class for processing CTD (conductivity/temperature/depth) data.

    Contains methods for loading data from source files, preprocessing, and calculating thermodynamic quantities from
    CTD observations.

    The core functionality is a *very limited* port of the Gibbs SeaWater (GSW) Oceanographic Toolbox (TEOS-10,
    https://www.teos-10.org). Only the equation of state and directly derived quantities are implemented. Variable names
    generally follow the GSW conventions for consistency with the source code.

    Burst dictionary conventions
    ----------------------------
    Variables in a burst dict are assumed to be 2-D arrays of shape (n_heights, n_samples), where the first axis
    corresponds to instrument depths (length self.n_heights) and the second axis is time. The individual thermodynamic
    methods accept any Numeric type and broadcast over these arrays without modification.

    Standard burst dict input keys recognized by `CTD.derive`:

        sp  : practical salinity (PSS-78)                         (unitless)
        t   : in-situ temperature                                    (deg C)
        p   : sea pressure (absolute pressure - 10.1325 dbar)         (dbar)
        lat : latitude                                   (deg N) -- optional

    Output keys added by `CTD.derive`:

        sa          : Absolute Salinity                               (g/kg)
        ct          : Conservative Temperature                       (deg C)
        rho         : in-situ density                               (kg/m^3)
        sigma0      : potential density anomaly ref 0 dbar           (kg/m^3)
        alpha       : thermal expansion coefficient                    (1/K)
        beta        : haline contraction coefficient                  (kg/g)
        sound_speed : speed of sound                                   (m/s)
        t_freezing  : in-situ freezing temperature                   (deg C)
        cp          : isobaric heat capacity                       (J/(kg K))
        nu          : kinematic viscosity                            (m^2/s)
        N2          : buoyancy frequency squared (n_heights > 1)     (1/s^2)
        z           : depth (positive downward)                          (m)

    References
    ----------
    IOC, SCOR and IAPSO, 2010: The international thermodynamic equation of seawater - 2010. Intergovernmental
        Oceanographic Commission, Manuals and Guides No. 56, UNESCO.

    Roquet, F., G. Madec, T.J. McDougall, P.M. Barker, 2015: Accurate polynomial expressions for the density and
        specific volume of seawater using the TEOS-10 standard. Ocean Modelling, 90, 29-43.

    McDougall, T.J. and P.M. Barker, 2011: Getting started with TEOS-10 and the Gibbs Seawater (GSW) Oceanographic
        Toolbox. SCOR/IAPSO WG 127(532), 1-28.
    """

    def __init__(
        self,
        files: str | list,
        name_map: dict,
        deployment_type: str = "fixed",
        fs: float | None = None,
        z: float | list[float] | None = None,
        z_convention: ZConvention = ZConvention.DEPTH,
        data_keys: str | list[str] | None = None,
        burst_dim: str | None = None,
        **loader_kwargs: Any,
    ) -> None:
        """Initialize a CTD object.

        Parameters
        ----------
        files : str or List[str]
            Path(s) to data files. If a list, each element is treated as a file containing data from an individual burst
            period. Supported formats: .npy (saved as a dict), .mat (saved as a MATLAB struct), .csv (variables in
            columns), or .nc (must specify `burst_dim` argument if this is a single file containing multiple bursts). If
            variables are two-dimensional, the larger dimension is assumed to be time and the shorter dimension a
            vertical coordinate.
        name_map : dict
            Mapping of standard variable names to names in the data files, e.g.:

            ```
            {
                "sp": "salinity variable name",
                "t":  "temperature variable name",
                "p":  "pressure variable name",
                "time": "time variable name",
            }
            ```

            Each value in the mapping may take one of three forms:

            - **str**: name of a single variable in the data file.
            - **list of str**: multiple variable names, used when data from multiple instruments are stored in
              separate variables rather than a 2-D array.
            - **callable**: a function applied to the loaded data object. Useful for unit conversions or combining
              source variables, e.g. `"time": lambda data: data["doy"] + data["hour"] / 24`.
        deployment_type : str, optional
            One of {"fixed", "cast"} depending on how the instrument is deployed. Default is "fixed", in which case
            self.z will be converted to a constant numpy array of instrument deployment depths or measurement cell
            heights. If "cast", self.z will be set to None and vertical coordinates will be calculated as a data
            variable within individual measurement bursts.
        fs : float, optional
            Sampling frequency (Hz). If not provided, it will be inferred (and rounded to 2 decimal places) from the
            ``time`` variable.
        z : float or List[float], optional
            Instrument depth(s) (m). Defaults to integer indices if not specified.
        z_convention : ZConvention, optional
            Convention for vertical coordinate, one of `{"m_above_bed", "depth"}`. Default is `"depth"`. Note that even
            if set to `"m_above_bed"` that will *only* apply to `self.z`, and the `z` calculated per-burst by
            `CTD.depth_from_pressure` will remain in depth convention (positive downward).
        data_keys : str or List[str], optional
            One or more nested keys to traverse after loading the file (e.g. ``"Data"`` if the variables in name_map are
            stored at ``burst["Data"]["variable_name"]``).
        burst_dim : str, optional
            Name of the burst dimension inside a monolithic NetCDF file. When given, `files` must be a single `.nc`
            path; the file is opened lazily and each burst is exposed by slicing along this dimension. When None
            (default), each entry in `files` is treated as one burst.
        **loader_kwargs
            Additional keyword arguments forwarded to the underlying file reader selected by extension
            (`pd.read_csv` for `.csv`/`.dat`, `scipy.io.loadmat` for `.mat`, `numpy.load` for `.npy`,
            `xarray.open_dataset` for `.nc`). See `BaseInstrument.__init__`.

        Returns
        -------
        CTD
            Initialized CTD object.
        """
        files_list = files if isinstance(files, list) else [files]
        CTD.validate_inputs(files_list, name_map, fs, z, z_convention, data_keys)
        super().__init__(
            files,
            name_map,
            deployment_type=DeploymentType(deployment_type),
            fs=fs,
            z=z,
            z_convention=z_convention,
            data_keys=data_keys,
            burst_dim=burst_dim,
            **loader_kwargs,
        )

    @staticmethod
    def validate_inputs(
        files: str | list,
        name_map: dict,
        fs: int | float | None = None,
        z: float | int | list[float | int] | None = None,
        z_convention: ZConvention = ZConvention.DEPTH,
        data_keys: str | list[str] | None = None,
    ) -> None:
        files_list = [files] if isinstance(files, str) else files
        BaseInstrument.validate_common_inputs(files_list, name_map, fs, z, data_keys)

        if z_convention not in [ZConvention.MAB, ZConvention.DEPTH]:
            raise ValueError(
                f"Invalid value for `z_convention`: {z_convention}. Must be one of ['m_above_bed', 'depth']"
            )

    def set_preprocess_opts(self, opts: dict[str, Any]) -> None:
        """Enable preprocessing for all subsequent burst loads.

        Parameters
        ----------
        opts : dict
            Preprocessing options. Supported keys:

            despike : dict, optional

                Options for despiking. Supported keys:

                method : {'threshold', 'goring_nikora', 'recursive_gaussian'}
                    Despiking algorithm to apply.

                If ``{'method': 'goring_nikora', ...}``, additional keys:
                    remaining_spikes : int
                    max_iter : int
                    robust_statistics : bool

                If ``{'method': 'threshold', ...}``, additional keys:
                    threshold_min : float
                    threshold_max : float

                If ``{'method': 'recursive_gaussian', ...}``, additional keys:
                    alpha : float
                    max_iter : int
        """
        super().set_preprocess_opts(opts)

    def _apply_preprocessing(self, burst_data: Any, keys_to_process: list[str] | None = None) -> Any:
        burst_data = super()._apply_preprocessing(burst_data, keys_to_process=self._burst_var_keys)
        return burst_data

    def sa_from_sp(self, sp: Numeric) -> Numeric:
        """
        Absolute Salinity from Practical Salinity using the constant-ratio approximation (gsw_sa_from_sp.m, simplified).

        Uses sa = sp * (35.16504 / 35), which skips the geographic Absolute Salinity Anomaly (SAAR) correction. Typical
        error is  ~0.01 g/kg in the open ocean. Errors can reach ~0.1 g/kg in marginal seas (Baltic, Red Sea, Arctic
        shelf) where SAAR is significant.

        Parameters
        ----------
        sp : Numeric
            Practical Salinity (PSS-78) (unitless)

        Returns
        -------
        Numeric
            Absolute Salinity (g/kg)
        """
        return sea_thermo.sa_from_sp(sp)

    def ct_from_t(self, sa: Numeric, t: Numeric, p: Numeric) -> Numeric:
        """
        Conservative Temperature from in-situ temperature (gsw_ct_from_t.m).

        Computes potential temperature at p_ref = 0 dbar via two iterations of Newton's method using Gibbs-entropy
        polynomials, then converts to Conservative Temperature via the potential-enthalpy polynomial.

        Parameters
        ----------
        sa : Numeric
            Absolute Salinity (g/kg)
        t : Numeric
            In-situ temperature (ITS-90, deg C)
        p : Numeric
            Sea pressure (dbar)

        Returns
        -------
        Numeric
            Conservative Temperature (ITS-90, deg C)
        """
        return sea_thermo.ct_from_t(sa, t, p)

    ##############################################################################
    # 75-term equation of state (Roquet et al., 2015)
    # Coefficients and polynomial structure match gsw_specvol.m / gsw_rho.m /
    # gsw_alpha.m / gsw_beta.m / gsw_sound_speed.m / gsw_sigma0.m exactly.
    ##############################################################################

    def specific_volume(self, sa: Numeric, ct: Numeric, p: Numeric) -> Numeric:
        """Specific volume from the 75-term polynomial EOS (gsw_specvol.m).

        Parameters
        ----------
        sa : Numeric
            Absolute Salinity (g/kg)
        ct : Numeric
            Conservative Temperature (deg C)
        p : Numeric
            Sea pressure (dbar)

        Returns
        -------
        Numeric
            Specific volume (m^3/kg)
        """
        return sea_thermo.specific_volume(sa, ct, p)

    def density(self, sa: Numeric, ct: Numeric, p: Numeric) -> Numeric:
        """In-situ density from the 75-term polynomial EOS (gsw_rho.m).

        Parameters
        ----------
        sa : Numeric
            Absolute Salinity (g/kg)
        ct : Numeric
            Conservative Temperature (deg C)
        p : Numeric
            Sea pressure (dbar)

        Returns
        -------
        Numeric
            In-situ density (kg/m^3)
        """
        return sea_thermo.density(sa, ct, p)

    def alpha(self, sa: Numeric, ct: Numeric, p: Numeric) -> Numeric:
        """Thermal expansion coefficient with respect to Conservative Temperature from the 75-term polynomial EOS
        (gsw_alpha.m).

        Parameters
        ----------
        sa : Numeric
            Absolute Salinity (g/kg)
        ct : Numeric
            Conservative Temperature (deg C)
        p : Numeric
            Sea pressure (dbar)

        Returns
        -------
        Numeric
            Thermal expansion coefficient (1/K)
        """
        return sea_thermo.alpha(sa, ct, p)

    def beta(self, sa: Numeric, ct: Numeric, p: Numeric) -> Numeric:
        """Haline contraction coefficient at constant Conservative Temperature from the 75-term polynomial EOS
        (gsw_beta.m).

        Parameters
        ----------
        sa : Numeric
            Absolute Salinity (g/kg)
        ct : Numeric
            Conservative Temperature (deg C)
        p : Numeric
            Sea pressure (dbar)

        Returns
        -------
        Numeric
            Haline contraction coefficient (kg/g)
        """
        return sea_thermo.beta(sa, ct, p)

    def sound_speed(self, sa: Numeric, ct: Numeric, p: Numeric) -> Numeric:
        """Speed of sound in seawater from the 75-term polynomial EOS (gsw_sound_speed.m).

        Parameters
        ----------
        sa : Numeric
            Absolute Salinity (g/kg)
        ct : Numeric
            Conservative Temperature (deg C)
        p : Numeric
            Sea pressure (dbar)

        Returns
        -------
        Numeric
            Speed of sound (m/s)
        """
        return sea_thermo.sound_speed(sa, ct, p)

    def sigma0(self, sa: Numeric, ct: Numeric) -> Numeric:
        """Potential density anomaly referenced to 0 dbar from the 75-term EOS (gsw_sigma0.m). Equal to potential
        density minus 1000 kg/m^3.

        Parameters
        ----------
        sa : Numeric
            Absolute Salinity (g/kg)
        ct : Numeric
            Conservative Temperature (deg C)

        Returns
        -------
        Numeric
            Potential density anomaly (kg/m^3)
        """
        return sea_thermo.sigma0(sa, ct)

    def freezing_temperature(self, sa: Numeric, p: Numeric) -> Numeric:
        """In-situ freezing temperature from a direct polynomial fit (gsw_t_freezing_poly.m).

        Uses the 23-coefficient polynomial given in the comments of gsw_t_freezing_poly.m, which avoids calling
        CT_freezing and t_from_CT. Error is between -8e-4 K and +3e-4 K compared with the exact Newton-Raphson method.

        Parameters
        ----------
        sa : Numeric
            Absolute Salinity (g/kg)
        p : Numeric
            Sea pressure (dbar)

        Returns
        -------
        Numeric
            Freezing temperature (deg C)
        """
        return sea_thermo.freezing_temperature(sa, p)

    def heat_capacity(self, sa: Numeric, t: Numeric, p: Numeric) -> Numeric:
        """
        Isobaric specific heat capacity of seawater (Fofonoff, 1985, Table 7).

        C_p(S, t, p) = A + B*S + C*S^(3/2)
                     + (D + E*S + F*S^(3/2)) * p
                     + (G + H*S + I*S^(3/2)) * p^2
                     + (J + K*S + M*S^(3/2)) * p^3

        where each letter coefficient is a polynomial in temperature t, and S is
        Practical Salinity (PSS-78), p is in bars.

        Parameters
        ----------
        sa : Numeric
            Absolute Salinity (g/kg)
        t : Numeric
            In-situ temperature (deg C)
        p : Numeric
            Sea pressure (dbar)

        Returns
        -------
        Numeric
            Isobaric heat capacity (J/(kg K))

        References
        ----------
        Fofonoff, N.P., 1985: Physical properties of seawater: A new salinity scale and equation of state for seawater.
            J. Geophys. Res., 90, 3332-3342.
        """
        return sea_thermo.heat_capacity(sa, t, p)

    def dynamic_viscosity(self, t: Numeric, sa: Numeric) -> Numeric:
        """Dynamic viscosity of seawater (Sharqawy et al., 2010).

        Parameters
        ----------
        t : Numeric
            In-situ temperature (deg C)
        sa : Numeric
            Absolute Salinity (g/kg)

        Returns
        -------
        Numeric
            Dynamic viscosity (Pa s)

        References
        ----------
        Sharqawy, M. H., Lienhard, J. H., & Zubair, S. M. (2010). Thermophysical properties of seawater: a review of
            existing correlations and data. Desalination and water treatment, 16(1-3), 354-380.
        """
        return sea_thermo.dynamic_viscosity(t, sa)

    def kinematic_viscosity(self, t: Numeric, sa: Numeric) -> Numeric:
        """Kinematic viscosity of seawater.

        Parameters
        ----------
        t : Numeric
            In-situ temperature (deg C)
        sa : Numeric
            Absolute Salinity (g/kg)

        Returns
        -------
        Numeric
            Kinematic viscosity (m^2/s)
        """
        return sea_thermo.kinematic_viscosity(t, sa)

    def thermal_conductivity(self, sa: Numeric, t: Numeric, p: Numeric) -> Numeric:
        """Thermal conductivity of seawater (Sharqawy et al., 2010, Eq. 14).

        Parameters
        ----------
        sa : Numeric
            Absolute Salinity (g/kg)
        t : Numeric
            In-situ temperature (deg C)
        p : Numeric
            Sea pressure (dbar)

        Returns
        -------
        Numeric
            Thermal conductivity (W/(m K))

        References
        ----------
        Sharqawy, M. H., Lienhard, J. H., & Zubair, S. M. (2010). Thermophysical properties of seawater: a review of
            existing correlations and data. Desalination and water treatment, 16(1-3), 354-380.
        """
        return sea_thermo.thermal_conductivity(sa, t, p)

    def buoyancy_frequency(self, sa: np.ndarray, ct: np.ndarray, p: np.ndarray, axis: int = 0) -> np.ndarray:
        """
        Squared buoyancy (Brunt-Vaisala) frequency from a vertical profile.

        Implements the TEOS-10 / GSW formula (Roquet et al., 2015):

        N^2 = g^2 / (specvol_mid * 1e4 * dp) * (beta*dSA - alpha*dCT)

        where dp is in dbar and the 1e4 factor converts to Pa.  N^2 is evaluated at mid-pressure points between adjacent
        levels, so the output has length n_heights - 1 along axis 0. The sign convention is consistent with GSW, in that
        N^2 > 0 corresponds to stable stratification.

        Requires n_heights > 1 (i.e., the mooring/cast must have measurements at more than one depth).

        Parameters
        ----------
        sa : np.ndarray
            Absolute Salinity, shape (n_heights, n_samples) (g/kg)
        ct : np.ndarray
            Conservative Temperature, shape (n_heights, n_samples) (deg C)
        p : np.ndarray
            Sea pressure, shape (n_heights, n_samples) (dbar)

        Returns
        -------
        np.ndarray
            N^2 at mid-depth levels, shape (n_heights - 1, n_samples) (1/s^2)
        """
        return sea_thermo.buoyancy_frequency(sa, ct, p, axis)

    def depth_from_pressure(self, p: Numeric, lat: Numeric | None = None) -> Numeric:
        """
        Depth from sea pressure using the UNESCO (1983) formula with optional latitude-dependent gravity. Depth is
        returned as a positive quantity (distance below surface).

        Parameters
        ----------
        p : Numeric
            Sea pressure (dbar)
        lat : Numeric, optional
            Latitude (degrees north). If not provided, g = 9.81 m/s^2 is used.

        Returns
        -------
        Numeric
            Depth (positive downward, m)
        """
        return sea_thermo.depth_from_pressure(p, lat)

    def pressure_from_depth(self, z: Numeric, lat: Numeric | None = None) -> Numeric:
        """
        Sea pressure from depth (positive downward) using a one-step Newton refinement of a hydrostatic initial guess.

        Parameters
        ----------
        z : Numeric
            Depth (positive downward, m)
        lat : Numeric, optional
            Latitude (degrees north). If not provided, g = 9.81 m/s^2 is used.

        Returns
        -------
        Numeric
            Sea pressure (dbar)
        """
        return sea_thermo.pressure_from_depth(z, lat)

    def derive(self, burst_data: dict[str, np.ndarray]) -> dict[str, np.ndarray]:
        """Compute all thermodynamic quantities derivable from the variables present in a burst dictionary, and return
        the burst dictionary augmented with those results.

        Each quantity is computed only when all of its required inputs are available as keys in ``burst_data``. The
        method never raises for missing inputs -- it simply skips any quantities it cannot compute.

        Input keys recognized
        ----------------------

            sp  : Practical Salinity (PSS-78)                                      (unitless)
            t   : in-situ temperature                                                 (deg C)
            p   : sea pressure                                                         (dbar)
            lat : latitude                                (deg N) -- optional, used for depth

        Output keys added to burst_data
        --------------------------------

            sa          : Absolute Salinity (g/kg)             -- requires sp
            ct          : Conservative Temperature (deg C)     -- requires sa, t, p
            rho         : in-situ density (kg/m^3)             -- requires sa, ct, p
            sigma0      : potential density anomaly (kg/m^3)   -- requires sa, ct
            alpha       : thermal expansion (1/K)              -- requires sa, ct, p
            beta        : haline contraction (kg/g)            -- requires sa, ct, p
            sound_speed : speed of sound (m/s)                 -- requires sa, ct, p
            t_freezing  : freezing temperature (deg C)         -- requires sa, p
            cp          : isobaric heat capacity (J/(kg K))    -- requires sa, t, p
            nu          : kinematic viscosity (m^2/s)          -- requires t, sa
            N2          : buoyancy frequency^2 (1/s^2)         -- requires sa, ct, p
                          (only computed when n_heights > 1)
            z           : depth (positive downward) (m)        -- requires p

        Parameters
        ----------
        burst_data : dict
            Burst dictionary, Modified in-place and also returned. If `self.deployment_type == "fixed"`, arrays are
            expected to have shape (n_heights, n_samples). If `self.deployment_type == "cast"`, arrays are expected to
            have shape (n_instruments, n_samples) where n_instruments is the number of sensors/data streams stored in
            each data variable from the source files.

        Returns
        -------
        dict
            The input ``burst_data`` dictionary with derived quantities added.
        """
        sp = burst_data.get("sp")
        t = burst_data.get("t")
        p = burst_data.get("p")
        lat = burst_data.get("lat")

        sa: np.ndarray | None = None
        if sp is not None:
            sa = np.asarray(self.sa_from_sp(sp))
            burst_data["sa"] = sa

        ct: np.ndarray | None = None
        if sa is not None and t is not None and p is not None:
            ct = np.asarray(self.ct_from_t(sa, t, p))
            burst_data["ct"] = ct

        if sa is not None and ct is not None and p is not None:
            burst_data["rho"] = np.asarray(self.density(sa, ct, p))
            burst_data["alpha"] = np.asarray(self.alpha(sa, ct, p))
            burst_data["beta"] = np.asarray(self.beta(sa, ct, p))
            burst_data["sound_speed"] = np.asarray(self.sound_speed(sa, ct, p))

        if sa is not None and ct is not None:
            burst_data["sigma0"] = np.asarray(self.sigma0(sa, ct))

        if sa is not None and p is not None:
            burst_data["t_freezing"] = np.asarray(self.freezing_temperature(sa, p))

        if sa is not None and t is not None and p is not None:
            burst_data["cp"] = np.asarray(self.heat_capacity(sa, t, p))

        if sa is not None and t is not None:
            burst_data["nu"] = np.asarray(self.kinematic_viscosity(t, sa))

        if sa is not None and ct is not None and p is not None:
            if self.z is not None and self.n_heights > 1:
                burst_data["N2"] = self.buoyancy_frequency(sa, ct, p, axis=0)
            elif self.z is None:
                burst_data["N2"] = self.buoyancy_frequency(sa, ct, p, axis=1)

        if p is not None:
            burst_data["z"] = np.asarray(self.depth_from_pressure(p, lat))

        return burst_data

    @property
    def _burst_var_keys(self) -> list[str]:
        return [k for k in self.name_map if k != "time"]

    def subsample(self, start_idx: int, end_idx: int) -> "CTD":
        new_ctd = self.__class__(
            files=self.files[start_idx:end_idx],
            name_map=self.name_map,
            deployment_type=self.deployment_type,
            fs=self.fs,
            z=self.z,
            data_keys=self.data_keys,
        )
        if self._preprocess_enabled:
            new_ctd.set_preprocess_opts(self._preprocess_opts)
        return new_ctd

__init__

__init__(files, name_map, deployment_type='fixed', fs=None, z=None, z_convention=DEPTH, data_keys=None, burst_dim=None, **loader_kwargs)

Initialize a CTD object.

Parameters:

Name Type Description Default
files str or List[str]

Path(s) to data files. If a list, each element is treated as a file containing data from an individual burst period. Supported formats: .npy (saved as a dict), .mat (saved as a MATLAB struct), .csv (variables in columns), or .nc (must specify burst_dim argument if this is a single file containing multiple bursts). If variables are two-dimensional, the larger dimension is assumed to be time and the shorter dimension a vertical coordinate.

required
name_map dict

Mapping of standard variable names to names in the data files, e.g.:

{
    "sp": "salinity variable name",
    "t":  "temperature variable name",
    "p":  "pressure variable name",
    "time": "time variable name",
}

Each value in the mapping may take one of three forms:

  • str: name of a single variable in the data file.
  • list of str: multiple variable names, used when data from multiple instruments are stored in separate variables rather than a 2-D array.
  • callable: a function applied to the loaded data object. Useful for unit conversions or combining source variables, e.g. "time": lambda data: data["doy"] + data["hour"] / 24.
required
deployment_type str

One of {"fixed", "cast"} depending on how the instrument is deployed. Default is "fixed", in which case self.z will be converted to a constant numpy array of instrument deployment depths or measurement cell heights. If "cast", self.z will be set to None and vertical coordinates will be calculated as a data variable within individual measurement bursts.

'fixed'
fs float

Sampling frequency (Hz). If not provided, it will be inferred (and rounded to 2 decimal places) from the time variable.

None
z float or List[float]

Instrument depth(s) (m). Defaults to integer indices if not specified.

None
z_convention ZConvention

Convention for vertical coordinate, one of {"m_above_bed", "depth"}. Default is "depth". Note that even if set to "m_above_bed" that will only apply to self.z, and the z calculated per-burst by CTD.depth_from_pressure will remain in depth convention (positive downward).

DEPTH
data_keys str or List[str]

One or more nested keys to traverse after loading the file (e.g. "Data" if the variables in name_map are stored at burst["Data"]["variable_name"]).

None
burst_dim str

Name of the burst dimension inside a monolithic NetCDF file. When given, files must be a single .nc path; the file is opened lazily and each burst is exposed by slicing along this dimension. When None (default), each entry in files is treated as one burst.

None
**loader_kwargs Any

Additional keyword arguments forwarded to the underlying file reader selected by extension (pd.read_csv for .csv/.dat, scipy.io.loadmat for .mat, numpy.load for .npy, xarray.open_dataset for .nc). See BaseInstrument.__init__.

{}

Returns:

Type Description
CTD

Initialized CTD object.

Source code in src/pytoast/ocean/ctd.py
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 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
132
133
134
135
136
137
138
139
140
141
142
143
144
def __init__(
    self,
    files: str | list,
    name_map: dict,
    deployment_type: str = "fixed",
    fs: float | None = None,
    z: float | list[float] | None = None,
    z_convention: ZConvention = ZConvention.DEPTH,
    data_keys: str | list[str] | None = None,
    burst_dim: str | None = None,
    **loader_kwargs: Any,
) -> None:
    """Initialize a CTD object.

    Parameters
    ----------
    files : str or List[str]
        Path(s) to data files. If a list, each element is treated as a file containing data from an individual burst
        period. Supported formats: .npy (saved as a dict), .mat (saved as a MATLAB struct), .csv (variables in
        columns), or .nc (must specify `burst_dim` argument if this is a single file containing multiple bursts). If
        variables are two-dimensional, the larger dimension is assumed to be time and the shorter dimension a
        vertical coordinate.
    name_map : dict
        Mapping of standard variable names to names in the data files, e.g.:

        ```
        {
            "sp": "salinity variable name",
            "t":  "temperature variable name",
            "p":  "pressure variable name",
            "time": "time variable name",
        }
        ```

        Each value in the mapping may take one of three forms:

        - **str**: name of a single variable in the data file.
        - **list of str**: multiple variable names, used when data from multiple instruments are stored in
          separate variables rather than a 2-D array.
        - **callable**: a function applied to the loaded data object. Useful for unit conversions or combining
          source variables, e.g. `"time": lambda data: data["doy"] + data["hour"] / 24`.
    deployment_type : str, optional
        One of {"fixed", "cast"} depending on how the instrument is deployed. Default is "fixed", in which case
        self.z will be converted to a constant numpy array of instrument deployment depths or measurement cell
        heights. If "cast", self.z will be set to None and vertical coordinates will be calculated as a data
        variable within individual measurement bursts.
    fs : float, optional
        Sampling frequency (Hz). If not provided, it will be inferred (and rounded to 2 decimal places) from the
        ``time`` variable.
    z : float or List[float], optional
        Instrument depth(s) (m). Defaults to integer indices if not specified.
    z_convention : ZConvention, optional
        Convention for vertical coordinate, one of `{"m_above_bed", "depth"}`. Default is `"depth"`. Note that even
        if set to `"m_above_bed"` that will *only* apply to `self.z`, and the `z` calculated per-burst by
        `CTD.depth_from_pressure` will remain in depth convention (positive downward).
    data_keys : str or List[str], optional
        One or more nested keys to traverse after loading the file (e.g. ``"Data"`` if the variables in name_map are
        stored at ``burst["Data"]["variable_name"]``).
    burst_dim : str, optional
        Name of the burst dimension inside a monolithic NetCDF file. When given, `files` must be a single `.nc`
        path; the file is opened lazily and each burst is exposed by slicing along this dimension. When None
        (default), each entry in `files` is treated as one burst.
    **loader_kwargs
        Additional keyword arguments forwarded to the underlying file reader selected by extension
        (`pd.read_csv` for `.csv`/`.dat`, `scipy.io.loadmat` for `.mat`, `numpy.load` for `.npy`,
        `xarray.open_dataset` for `.nc`). See `BaseInstrument.__init__`.

    Returns
    -------
    CTD
        Initialized CTD object.
    """
    files_list = files if isinstance(files, list) else [files]
    CTD.validate_inputs(files_list, name_map, fs, z, z_convention, data_keys)
    super().__init__(
        files,
        name_map,
        deployment_type=DeploymentType(deployment_type),
        fs=fs,
        z=z,
        z_convention=z_convention,
        data_keys=data_keys,
        burst_dim=burst_dim,
        **loader_kwargs,
    )

alpha

alpha(sa, ct, p)

Thermal expansion coefficient with respect to Conservative Temperature from the 75-term polynomial EOS (gsw_alpha.m).

Parameters:

Name Type Description Default
sa Numeric

Absolute Salinity (g/kg)

required
ct Numeric

Conservative Temperature (deg C)

required
p Numeric

Sea pressure (dbar)

required

Returns:

Type Description
Numeric

Thermal expansion coefficient (1/K)

Source code in src/pytoast/ocean/ctd.py
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
def alpha(self, sa: Numeric, ct: Numeric, p: Numeric) -> Numeric:
    """Thermal expansion coefficient with respect to Conservative Temperature from the 75-term polynomial EOS
    (gsw_alpha.m).

    Parameters
    ----------
    sa : Numeric
        Absolute Salinity (g/kg)
    ct : Numeric
        Conservative Temperature (deg C)
    p : Numeric
        Sea pressure (dbar)

    Returns
    -------
    Numeric
        Thermal expansion coefficient (1/K)
    """
    return sea_thermo.alpha(sa, ct, p)

beta

beta(sa, ct, p)

Haline contraction coefficient at constant Conservative Temperature from the 75-term polynomial EOS (gsw_beta.m).

Parameters:

Name Type Description Default
sa Numeric

Absolute Salinity (g/kg)

required
ct Numeric

Conservative Temperature (deg C)

required
p Numeric

Sea pressure (dbar)

required

Returns:

Type Description
Numeric

Haline contraction coefficient (kg/g)

Source code in src/pytoast/ocean/ctd.py
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
def beta(self, sa: Numeric, ct: Numeric, p: Numeric) -> Numeric:
    """Haline contraction coefficient at constant Conservative Temperature from the 75-term polynomial EOS
    (gsw_beta.m).

    Parameters
    ----------
    sa : Numeric
        Absolute Salinity (g/kg)
    ct : Numeric
        Conservative Temperature (deg C)
    p : Numeric
        Sea pressure (dbar)

    Returns
    -------
    Numeric
        Haline contraction coefficient (kg/g)
    """
    return sea_thermo.beta(sa, ct, p)

buoyancy_frequency

buoyancy_frequency(sa, ct, p, axis=0)

Squared buoyancy (Brunt-Vaisala) frequency from a vertical profile.

Implements the TEOS-10 / GSW formula (Roquet et al., 2015):

N^2 = g^2 / (specvol_mid * 1e4 * dp) * (betadSA - alphadCT)

where dp is in dbar and the 1e4 factor converts to Pa. N^2 is evaluated at mid-pressure points between adjacent levels, so the output has length n_heights - 1 along axis 0. The sign convention is consistent with GSW, in that N^2 > 0 corresponds to stable stratification.

Requires n_heights > 1 (i.e., the mooring/cast must have measurements at more than one depth).

Parameters:

Name Type Description Default
sa ndarray

Absolute Salinity, shape (n_heights, n_samples) (g/kg)

required
ct ndarray

Conservative Temperature, shape (n_heights, n_samples) (deg C)

required
p ndarray

Sea pressure, shape (n_heights, n_samples) (dbar)

required

Returns:

Type Description
ndarray

N^2 at mid-depth levels, shape (n_heights - 1, n_samples) (1/s^2)

Source code in src/pytoast/ocean/ctd.py
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
def buoyancy_frequency(self, sa: np.ndarray, ct: np.ndarray, p: np.ndarray, axis: int = 0) -> np.ndarray:
    """
    Squared buoyancy (Brunt-Vaisala) frequency from a vertical profile.

    Implements the TEOS-10 / GSW formula (Roquet et al., 2015):

    N^2 = g^2 / (specvol_mid * 1e4 * dp) * (beta*dSA - alpha*dCT)

    where dp is in dbar and the 1e4 factor converts to Pa.  N^2 is evaluated at mid-pressure points between adjacent
    levels, so the output has length n_heights - 1 along axis 0. The sign convention is consistent with GSW, in that
    N^2 > 0 corresponds to stable stratification.

    Requires n_heights > 1 (i.e., the mooring/cast must have measurements at more than one depth).

    Parameters
    ----------
    sa : np.ndarray
        Absolute Salinity, shape (n_heights, n_samples) (g/kg)
    ct : np.ndarray
        Conservative Temperature, shape (n_heights, n_samples) (deg C)
    p : np.ndarray
        Sea pressure, shape (n_heights, n_samples) (dbar)

    Returns
    -------
    np.ndarray
        N^2 at mid-depth levels, shape (n_heights - 1, n_samples) (1/s^2)
    """
    return sea_thermo.buoyancy_frequency(sa, ct, p, axis)

ct_from_t

ct_from_t(sa, t, p)

Conservative Temperature from in-situ temperature (gsw_ct_from_t.m).

Computes potential temperature at p_ref = 0 dbar via two iterations of Newton's method using Gibbs-entropy polynomials, then converts to Conservative Temperature via the potential-enthalpy polynomial.

Parameters:

Name Type Description Default
sa Numeric

Absolute Salinity (g/kg)

required
t Numeric

In-situ temperature (ITS-90, deg C)

required
p Numeric

Sea pressure (dbar)

required

Returns:

Type Description
Numeric

Conservative Temperature (ITS-90, deg C)

Source code in src/pytoast/ocean/ctd.py
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
def ct_from_t(self, sa: Numeric, t: Numeric, p: Numeric) -> Numeric:
    """
    Conservative Temperature from in-situ temperature (gsw_ct_from_t.m).

    Computes potential temperature at p_ref = 0 dbar via two iterations of Newton's method using Gibbs-entropy
    polynomials, then converts to Conservative Temperature via the potential-enthalpy polynomial.

    Parameters
    ----------
    sa : Numeric
        Absolute Salinity (g/kg)
    t : Numeric
        In-situ temperature (ITS-90, deg C)
    p : Numeric
        Sea pressure (dbar)

    Returns
    -------
    Numeric
        Conservative Temperature (ITS-90, deg C)
    """
    return sea_thermo.ct_from_t(sa, t, p)

density

density(sa, ct, p)

In-situ density from the 75-term polynomial EOS (gsw_rho.m).

Parameters:

Name Type Description Default
sa Numeric

Absolute Salinity (g/kg)

required
ct Numeric

Conservative Temperature (deg C)

required
p Numeric

Sea pressure (dbar)

required

Returns:

Type Description
Numeric

In-situ density (kg/m^3)

Source code in src/pytoast/ocean/ctd.py
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
def density(self, sa: Numeric, ct: Numeric, p: Numeric) -> Numeric:
    """In-situ density from the 75-term polynomial EOS (gsw_rho.m).

    Parameters
    ----------
    sa : Numeric
        Absolute Salinity (g/kg)
    ct : Numeric
        Conservative Temperature (deg C)
    p : Numeric
        Sea pressure (dbar)

    Returns
    -------
    Numeric
        In-situ density (kg/m^3)
    """
    return sea_thermo.density(sa, ct, p)

depth_from_pressure

depth_from_pressure(p, lat=None)

Depth from sea pressure using the UNESCO (1983) formula with optional latitude-dependent gravity. Depth is returned as a positive quantity (distance below surface).

Parameters:

Name Type Description Default
p Numeric

Sea pressure (dbar)

required
lat Numeric

Latitude (degrees north). If not provided, g = 9.81 m/s^2 is used.

None

Returns:

Type Description
Numeric

Depth (positive downward, m)

Source code in src/pytoast/ocean/ctd.py
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
def depth_from_pressure(self, p: Numeric, lat: Numeric | None = None) -> Numeric:
    """
    Depth from sea pressure using the UNESCO (1983) formula with optional latitude-dependent gravity. Depth is
    returned as a positive quantity (distance below surface).

    Parameters
    ----------
    p : Numeric
        Sea pressure (dbar)
    lat : Numeric, optional
        Latitude (degrees north). If not provided, g = 9.81 m/s^2 is used.

    Returns
    -------
    Numeric
        Depth (positive downward, m)
    """
    return sea_thermo.depth_from_pressure(p, lat)

derive

derive(burst_data)

Compute all thermodynamic quantities derivable from the variables present in a burst dictionary, and return the burst dictionary augmented with those results.

Each quantity is computed only when all of its required inputs are available as keys in burst_data. The method never raises for missing inputs -- it simply skips any quantities it cannot compute.

Input keys recognized
sp  : Practical Salinity (PSS-78)                                      (unitless)
t   : in-situ temperature                                                 (deg C)
p   : sea pressure                                                         (dbar)
lat : latitude                                (deg N) -- optional, used for depth
Output keys added to burst_data
sa          : Absolute Salinity (g/kg)             -- requires sp
ct          : Conservative Temperature (deg C)     -- requires sa, t, p
rho         : in-situ density (kg/m^3)             -- requires sa, ct, p
sigma0      : potential density anomaly (kg/m^3)   -- requires sa, ct
alpha       : thermal expansion (1/K)              -- requires sa, ct, p
beta        : haline contraction (kg/g)            -- requires sa, ct, p
sound_speed : speed of sound (m/s)                 -- requires sa, ct, p
t_freezing  : freezing temperature (deg C)         -- requires sa, p
cp          : isobaric heat capacity (J/(kg K))    -- requires sa, t, p
nu          : kinematic viscosity (m^2/s)          -- requires t, sa
N2          : buoyancy frequency^2 (1/s^2)         -- requires sa, ct, p
              (only computed when n_heights > 1)
z           : depth (positive downward) (m)        -- requires p

Parameters:

Name Type Description Default
burst_data dict

Burst dictionary, Modified in-place and also returned. If self.deployment_type == "fixed", arrays are expected to have shape (n_heights, n_samples). If self.deployment_type == "cast", arrays are expected to have shape (n_instruments, n_samples) where n_instruments is the number of sensors/data streams stored in each data variable from the source files.

required

Returns:

Type Description
dict

The input burst_data dictionary with derived quantities added.

Source code in src/pytoast/ocean/ctd.py
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
def derive(self, burst_data: dict[str, np.ndarray]) -> dict[str, np.ndarray]:
    """Compute all thermodynamic quantities derivable from the variables present in a burst dictionary, and return
    the burst dictionary augmented with those results.

    Each quantity is computed only when all of its required inputs are available as keys in ``burst_data``. The
    method never raises for missing inputs -- it simply skips any quantities it cannot compute.

    Input keys recognized
    ----------------------

        sp  : Practical Salinity (PSS-78)                                      (unitless)
        t   : in-situ temperature                                                 (deg C)
        p   : sea pressure                                                         (dbar)
        lat : latitude                                (deg N) -- optional, used for depth

    Output keys added to burst_data
    --------------------------------

        sa          : Absolute Salinity (g/kg)             -- requires sp
        ct          : Conservative Temperature (deg C)     -- requires sa, t, p
        rho         : in-situ density (kg/m^3)             -- requires sa, ct, p
        sigma0      : potential density anomaly (kg/m^3)   -- requires sa, ct
        alpha       : thermal expansion (1/K)              -- requires sa, ct, p
        beta        : haline contraction (kg/g)            -- requires sa, ct, p
        sound_speed : speed of sound (m/s)                 -- requires sa, ct, p
        t_freezing  : freezing temperature (deg C)         -- requires sa, p
        cp          : isobaric heat capacity (J/(kg K))    -- requires sa, t, p
        nu          : kinematic viscosity (m^2/s)          -- requires t, sa
        N2          : buoyancy frequency^2 (1/s^2)         -- requires sa, ct, p
                      (only computed when n_heights > 1)
        z           : depth (positive downward) (m)        -- requires p

    Parameters
    ----------
    burst_data : dict
        Burst dictionary, Modified in-place and also returned. If `self.deployment_type == "fixed"`, arrays are
        expected to have shape (n_heights, n_samples). If `self.deployment_type == "cast"`, arrays are expected to
        have shape (n_instruments, n_samples) where n_instruments is the number of sensors/data streams stored in
        each data variable from the source files.

    Returns
    -------
    dict
        The input ``burst_data`` dictionary with derived quantities added.
    """
    sp = burst_data.get("sp")
    t = burst_data.get("t")
    p = burst_data.get("p")
    lat = burst_data.get("lat")

    sa: np.ndarray | None = None
    if sp is not None:
        sa = np.asarray(self.sa_from_sp(sp))
        burst_data["sa"] = sa

    ct: np.ndarray | None = None
    if sa is not None and t is not None and p is not None:
        ct = np.asarray(self.ct_from_t(sa, t, p))
        burst_data["ct"] = ct

    if sa is not None and ct is not None and p is not None:
        burst_data["rho"] = np.asarray(self.density(sa, ct, p))
        burst_data["alpha"] = np.asarray(self.alpha(sa, ct, p))
        burst_data["beta"] = np.asarray(self.beta(sa, ct, p))
        burst_data["sound_speed"] = np.asarray(self.sound_speed(sa, ct, p))

    if sa is not None and ct is not None:
        burst_data["sigma0"] = np.asarray(self.sigma0(sa, ct))

    if sa is not None and p is not None:
        burst_data["t_freezing"] = np.asarray(self.freezing_temperature(sa, p))

    if sa is not None and t is not None and p is not None:
        burst_data["cp"] = np.asarray(self.heat_capacity(sa, t, p))

    if sa is not None and t is not None:
        burst_data["nu"] = np.asarray(self.kinematic_viscosity(t, sa))

    if sa is not None and ct is not None and p is not None:
        if self.z is not None and self.n_heights > 1:
            burst_data["N2"] = self.buoyancy_frequency(sa, ct, p, axis=0)
        elif self.z is None:
            burst_data["N2"] = self.buoyancy_frequency(sa, ct, p, axis=1)

    if p is not None:
        burst_data["z"] = np.asarray(self.depth_from_pressure(p, lat))

    return burst_data

dynamic_viscosity

dynamic_viscosity(t, sa)

Dynamic viscosity of seawater (Sharqawy et al., 2010).

Parameters:

Name Type Description Default
t Numeric

In-situ temperature (deg C)

required
sa Numeric

Absolute Salinity (g/kg)

required

Returns:

Type Description
Numeric

Dynamic viscosity (Pa s)

References

Sharqawy, M. H., Lienhard, J. H., & Zubair, S. M. (2010). Thermophysical properties of seawater: a review of existing correlations and data. Desalination and water treatment, 16(1-3), 354-380.

Source code in src/pytoast/ocean/ctd.py
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
def dynamic_viscosity(self, t: Numeric, sa: Numeric) -> Numeric:
    """Dynamic viscosity of seawater (Sharqawy et al., 2010).

    Parameters
    ----------
    t : Numeric
        In-situ temperature (deg C)
    sa : Numeric
        Absolute Salinity (g/kg)

    Returns
    -------
    Numeric
        Dynamic viscosity (Pa s)

    References
    ----------
    Sharqawy, M. H., Lienhard, J. H., & Zubair, S. M. (2010). Thermophysical properties of seawater: a review of
        existing correlations and data. Desalination and water treatment, 16(1-3), 354-380.
    """
    return sea_thermo.dynamic_viscosity(t, sa)

freezing_temperature

freezing_temperature(sa, p)

In-situ freezing temperature from a direct polynomial fit (gsw_t_freezing_poly.m).

Uses the 23-coefficient polynomial given in the comments of gsw_t_freezing_poly.m, which avoids calling CT_freezing and t_from_CT. Error is between -8e-4 K and +3e-4 K compared with the exact Newton-Raphson method.

Parameters:

Name Type Description Default
sa Numeric

Absolute Salinity (g/kg)

required
p Numeric

Sea pressure (dbar)

required

Returns:

Type Description
Numeric

Freezing temperature (deg C)

Source code in src/pytoast/ocean/ctd.py
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
def freezing_temperature(self, sa: Numeric, p: Numeric) -> Numeric:
    """In-situ freezing temperature from a direct polynomial fit (gsw_t_freezing_poly.m).

    Uses the 23-coefficient polynomial given in the comments of gsw_t_freezing_poly.m, which avoids calling
    CT_freezing and t_from_CT. Error is between -8e-4 K and +3e-4 K compared with the exact Newton-Raphson method.

    Parameters
    ----------
    sa : Numeric
        Absolute Salinity (g/kg)
    p : Numeric
        Sea pressure (dbar)

    Returns
    -------
    Numeric
        Freezing temperature (deg C)
    """
    return sea_thermo.freezing_temperature(sa, p)

heat_capacity

heat_capacity(sa, t, p)

Isobaric specific heat capacity of seawater (Fofonoff, 1985, Table 7).

C_p(S, t, p) = A + BS + CS^(3/2) + (D + ES + FS^(3/2)) * p + (G + HS + IS^(3/2)) * p^2 + (J + KS + MS^(3/2)) * p^3

where each letter coefficient is a polynomial in temperature t, and S is Practical Salinity (PSS-78), p is in bars.

Parameters:

Name Type Description Default
sa Numeric

Absolute Salinity (g/kg)

required
t Numeric

In-situ temperature (deg C)

required
p Numeric

Sea pressure (dbar)

required

Returns:

Type Description
Numeric

Isobaric heat capacity (J/(kg K))

References

Fofonoff, N.P., 1985: Physical properties of seawater: A new salinity scale and equation of state for seawater. J. Geophys. Res., 90, 3332-3342.

Source code in src/pytoast/ocean/ctd.py
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
def heat_capacity(self, sa: Numeric, t: Numeric, p: Numeric) -> Numeric:
    """
    Isobaric specific heat capacity of seawater (Fofonoff, 1985, Table 7).

    C_p(S, t, p) = A + B*S + C*S^(3/2)
                 + (D + E*S + F*S^(3/2)) * p
                 + (G + H*S + I*S^(3/2)) * p^2
                 + (J + K*S + M*S^(3/2)) * p^3

    where each letter coefficient is a polynomial in temperature t, and S is
    Practical Salinity (PSS-78), p is in bars.

    Parameters
    ----------
    sa : Numeric
        Absolute Salinity (g/kg)
    t : Numeric
        In-situ temperature (deg C)
    p : Numeric
        Sea pressure (dbar)

    Returns
    -------
    Numeric
        Isobaric heat capacity (J/(kg K))

    References
    ----------
    Fofonoff, N.P., 1985: Physical properties of seawater: A new salinity scale and equation of state for seawater.
        J. Geophys. Res., 90, 3332-3342.
    """
    return sea_thermo.heat_capacity(sa, t, p)

kinematic_viscosity

kinematic_viscosity(t, sa)

Kinematic viscosity of seawater.

Parameters:

Name Type Description Default
t Numeric

In-situ temperature (deg C)

required
sa Numeric

Absolute Salinity (g/kg)

required

Returns:

Type Description
Numeric

Kinematic viscosity (m^2/s)

Source code in src/pytoast/ocean/ctd.py
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
def kinematic_viscosity(self, t: Numeric, sa: Numeric) -> Numeric:
    """Kinematic viscosity of seawater.

    Parameters
    ----------
    t : Numeric
        In-situ temperature (deg C)
    sa : Numeric
        Absolute Salinity (g/kg)

    Returns
    -------
    Numeric
        Kinematic viscosity (m^2/s)
    """
    return sea_thermo.kinematic_viscosity(t, sa)

pressure_from_depth

pressure_from_depth(z, lat=None)

Sea pressure from depth (positive downward) using a one-step Newton refinement of a hydrostatic initial guess.

Parameters:

Name Type Description Default
z Numeric

Depth (positive downward, m)

required
lat Numeric

Latitude (degrees north). If not provided, g = 9.81 m/s^2 is used.

None

Returns:

Type Description
Numeric

Sea pressure (dbar)

Source code in src/pytoast/ocean/ctd.py
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
def pressure_from_depth(self, z: Numeric, lat: Numeric | None = None) -> Numeric:
    """
    Sea pressure from depth (positive downward) using a one-step Newton refinement of a hydrostatic initial guess.

    Parameters
    ----------
    z : Numeric
        Depth (positive downward, m)
    lat : Numeric, optional
        Latitude (degrees north). If not provided, g = 9.81 m/s^2 is used.

    Returns
    -------
    Numeric
        Sea pressure (dbar)
    """
    return sea_thermo.pressure_from_depth(z, lat)

sa_from_sp

sa_from_sp(sp)

Absolute Salinity from Practical Salinity using the constant-ratio approximation (gsw_sa_from_sp.m, simplified).

Uses sa = sp * (35.16504 / 35), which skips the geographic Absolute Salinity Anomaly (SAAR) correction. Typical error is ~0.01 g/kg in the open ocean. Errors can reach ~0.1 g/kg in marginal seas (Baltic, Red Sea, Arctic shelf) where SAAR is significant.

Parameters:

Name Type Description Default
sp Numeric

Practical Salinity (PSS-78) (unitless)

required

Returns:

Type Description
Numeric

Absolute Salinity (g/kg)

Source code in src/pytoast/ocean/ctd.py
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
def sa_from_sp(self, sp: Numeric) -> Numeric:
    """
    Absolute Salinity from Practical Salinity using the constant-ratio approximation (gsw_sa_from_sp.m, simplified).

    Uses sa = sp * (35.16504 / 35), which skips the geographic Absolute Salinity Anomaly (SAAR) correction. Typical
    error is  ~0.01 g/kg in the open ocean. Errors can reach ~0.1 g/kg in marginal seas (Baltic, Red Sea, Arctic
    shelf) where SAAR is significant.

    Parameters
    ----------
    sp : Numeric
        Practical Salinity (PSS-78) (unitless)

    Returns
    -------
    Numeric
        Absolute Salinity (g/kg)
    """
    return sea_thermo.sa_from_sp(sp)

set_preprocess_opts

set_preprocess_opts(opts)

Enable preprocessing for all subsequent burst loads.

Parameters:

Name Type Description Default
opts dict

Preprocessing options. Supported keys:

despike : dict, optional

Options for despiking. Supported keys:

method : {'threshold', 'goring_nikora', 'recursive_gaussian'}
    Despiking algorithm to apply.

If ``{'method': 'goring_nikora', ...}``, additional keys:
    remaining_spikes : int
    max_iter : int
    robust_statistics : bool

If ``{'method': 'threshold', ...}``, additional keys:
    threshold_min : float
    threshold_max : float

If ``{'method': 'recursive_gaussian', ...}``, additional keys:
    alpha : float
    max_iter : int
required
Source code in src/pytoast/ocean/ctd.py
163
164
165
166
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
def set_preprocess_opts(self, opts: dict[str, Any]) -> None:
    """Enable preprocessing for all subsequent burst loads.

    Parameters
    ----------
    opts : dict
        Preprocessing options. Supported keys:

        despike : dict, optional

            Options for despiking. Supported keys:

            method : {'threshold', 'goring_nikora', 'recursive_gaussian'}
                Despiking algorithm to apply.

            If ``{'method': 'goring_nikora', ...}``, additional keys:
                remaining_spikes : int
                max_iter : int
                robust_statistics : bool

            If ``{'method': 'threshold', ...}``, additional keys:
                threshold_min : float
                threshold_max : float

            If ``{'method': 'recursive_gaussian', ...}``, additional keys:
                alpha : float
                max_iter : int
    """
    super().set_preprocess_opts(opts)

sigma0

sigma0(sa, ct)

Potential density anomaly referenced to 0 dbar from the 75-term EOS (gsw_sigma0.m). Equal to potential density minus 1000 kg/m^3.

Parameters:

Name Type Description Default
sa Numeric

Absolute Salinity (g/kg)

required
ct Numeric

Conservative Temperature (deg C)

required

Returns:

Type Description
Numeric

Potential density anomaly (kg/m^3)

Source code in src/pytoast/ocean/ctd.py
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def sigma0(self, sa: Numeric, ct: Numeric) -> Numeric:
    """Potential density anomaly referenced to 0 dbar from the 75-term EOS (gsw_sigma0.m). Equal to potential
    density minus 1000 kg/m^3.

    Parameters
    ----------
    sa : Numeric
        Absolute Salinity (g/kg)
    ct : Numeric
        Conservative Temperature (deg C)

    Returns
    -------
    Numeric
        Potential density anomaly (kg/m^3)
    """
    return sea_thermo.sigma0(sa, ct)

sound_speed

sound_speed(sa, ct, p)

Speed of sound in seawater from the 75-term polynomial EOS (gsw_sound_speed.m).

Parameters:

Name Type Description Default
sa Numeric

Absolute Salinity (g/kg)

required
ct Numeric

Conservative Temperature (deg C)

required
p Numeric

Sea pressure (dbar)

required

Returns:

Type Description
Numeric

Speed of sound (m/s)

Source code in src/pytoast/ocean/ctd.py
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
def sound_speed(self, sa: Numeric, ct: Numeric, p: Numeric) -> Numeric:
    """Speed of sound in seawater from the 75-term polynomial EOS (gsw_sound_speed.m).

    Parameters
    ----------
    sa : Numeric
        Absolute Salinity (g/kg)
    ct : Numeric
        Conservative Temperature (deg C)
    p : Numeric
        Sea pressure (dbar)

    Returns
    -------
    Numeric
        Speed of sound (m/s)
    """
    return sea_thermo.sound_speed(sa, ct, p)

specific_volume

specific_volume(sa, ct, p)

Specific volume from the 75-term polynomial EOS (gsw_specvol.m).

Parameters:

Name Type Description Default
sa Numeric

Absolute Salinity (g/kg)

required
ct Numeric

Conservative Temperature (deg C)

required
p Numeric

Sea pressure (dbar)

required

Returns:

Type Description
Numeric

Specific volume (m^3/kg)

Source code in src/pytoast/ocean/ctd.py
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
def specific_volume(self, sa: Numeric, ct: Numeric, p: Numeric) -> Numeric:
    """Specific volume from the 75-term polynomial EOS (gsw_specvol.m).

    Parameters
    ----------
    sa : Numeric
        Absolute Salinity (g/kg)
    ct : Numeric
        Conservative Temperature (deg C)
    p : Numeric
        Sea pressure (dbar)

    Returns
    -------
    Numeric
        Specific volume (m^3/kg)
    """
    return sea_thermo.specific_volume(sa, ct, p)

thermal_conductivity

thermal_conductivity(sa, t, p)

Thermal conductivity of seawater (Sharqawy et al., 2010, Eq. 14).

Parameters:

Name Type Description Default
sa Numeric

Absolute Salinity (g/kg)

required
t Numeric

In-situ temperature (deg C)

required
p Numeric

Sea pressure (dbar)

required

Returns:

Type Description
Numeric

Thermal conductivity (W/(m K))

References

Sharqawy, M. H., Lienhard, J. H., & Zubair, S. M. (2010). Thermophysical properties of seawater: a review of existing correlations and data. Desalination and water treatment, 16(1-3), 354-380.

Source code in src/pytoast/ocean/ctd.py
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def thermal_conductivity(self, sa: Numeric, t: Numeric, p: Numeric) -> Numeric:
    """Thermal conductivity of seawater (Sharqawy et al., 2010, Eq. 14).

    Parameters
    ----------
    sa : Numeric
        Absolute Salinity (g/kg)
    t : Numeric
        In-situ temperature (deg C)
    p : Numeric
        Sea pressure (dbar)

    Returns
    -------
    Numeric
        Thermal conductivity (W/(m K))

    References
    ----------
    Sharqawy, M. H., Lienhard, J. H., & Zubair, S. M. (2010). Thermophysical properties of seawater: a review of
        existing correlations and data. Desalination and water treatment, 16(1-3), 354-380.
    """
    return sea_thermo.thermal_conductivity(sa, t, p)