gsw/gsw_internal_funcs/
mod.rs

1//! Internal Functions
2//!
3//! Functions not intended to be used outside this library
4
5use crate::gsw_internal_const::{DB2PA, GSW_PU, GSW_SFAC};
6use crate::gsw_sp_coefficients::*;
7use crate::gsw_specvol_coefficients::{V005, V006};
8use crate::{Error, Result};
9
10const G0: f64 = 2.641_463_563_366_498e-1;
11const G1: f64 = 2.007_883_247_811_176e-4;
12const G2: f64 = -4.107_694_432_853_053e-6;
13const G3: f64 = 8.401_670_882_091_225e-8;
14const G4: f64 = -1.711_392_021_989_21e-9;
15const G5: f64 = 3.374_193_893_377_38e-11;
16const G6: f64 = -5.923_731_174_730_784e-13;
17const G7: f64 = 8.057_771_569_962_299e-15;
18const G8: f64 = -7.054_313_817_447_962e-17;
19const G9: f64 = 2.859_992_717_347_235e-19;
20
21#[inline]
22/// Non-dimensional pressure
23///
24/// The polynomial approximation solutions proposed by Roquet (2015) are based
25/// on non-dimensional salinity, temperature, and pressure. Here we scale
26/// pressure by p_u (1e4 [dbar]) to obtain the non-dimensional quantity \pi
27/// (TEOS-10 Manual appendix K, or \zeta on Roquet (2015)).
28///
29/// # Argument
30///
31/// * `p`: sea pressure \[dbar\] (i.e. absolute pressure - 10.1325 dbar)
32///
33/// # Returns
34///
35/// * `\pi`: Non-dimensional pressure
36///
37/// # Notes
38///
39/// * The original formulation is a scaling of p by p_u. The MatLab and C
40///   implementations of GSW operate as the product with 1e-4, which does make
41///   sense since it is a lighter operation than a division is for computers.
42///   The issue here is on the inability of f64 to fully represent certain
43///   fractions. For instance, while 3812.0 can be perfectly represented,
44///   0.3812 is rounded to 0.381200000000000038813. Similarly 1e4 is fine but
45///   1e-4 on f64 is rounded to 0.000100000000000000004792, thus 3812/1e4 is
46///   diffrent than 3812*1e-4.
47///
48/// # Example
49/// ```
50/// let p = 3812;
51/// assert!((3_812.0 * 1e-4) > (3_812.0 / 1e4))
52/// ```
53pub(crate) fn non_dimensional_p(p: f64) -> f64 {
54    if cfg!(feature = "compat") {
55        p * 1e-4
56    } else {
57        p / GSW_PU
58    }
59}
60
61/// Specific Volume of Standard Ocean Salinity and CT=0
62///
63/// This function calculates specific volume at the Standard Ocean Salinity,
64/// SSO, and at a Conservative Temperature of zero degrees C, as a function
65/// of pressure, p, in dbar, using a streamlined version of the 75-term CT
66/// version of specific volume, that is, a streamlined version of the code
67/// "specvol(SA,CT,p)".
68///
69/// version: 3.06.12
70///
71/// If using compat (truncated constants) there is a difference of O[1e-19],
72/// which is negligible but enough to fail the validation tests.
73pub fn specvol_sso_0(p: f64) -> f64 {
74    const VXX0: f64 = if cfg!(feature = "compat") {
75        9.726_613_854_843_87e-4
76    } else {
77        9.726_613_854_843_871e-4
78    };
79
80    const VXX1: f64 = if cfg!(feature = "compat") {
81        -4.505_913_211_160_929e-5
82    } else {
83        -4.505_913_211_160_931e-5
84    };
85
86    const VXX2: f64 = if cfg!(feature = "compat") {
87        7.130_728_965_927_127e-6
88    } else {
89        7.130_728_965_927_128e-6
90    };
91
92    const VXX3: f64 = if cfg!(feature = "compat") {
93        -6.657_179_479_768_312e-7
94    } else {
95        -6.657_179_479_768_313e-7
96    };
97    const VXX4: f64 = if cfg!(feature = "compat") {
98        -2.994_054_447_232_88e-8
99    } else {
100        -2.994_054_447_232_877_6e-8
101    };
102
103    let p = non_dimensional_p(p);
104    VXX0 + p * (VXX1 + p * (VXX2 + p * (VXX3 + p * (VXX4 + p * (V005 + V006 * p)))))
105}
106
107#[cfg(test)]
108mod test_specvol_sso_0 {
109    use super::specvol_sso_0;
110    use crate::gsw_internal_const::GSW_SSO;
111    use crate::volume::specvol;
112
113    /// specvol() at SSO & CT=0 should be identical to specvol_sso_0()
114    #[test]
115    fn specvol_vs_specvol_sso_0() {
116        let p_to_test: [f64; 5] = [0., 10., 100., 1_000., 5_000.];
117        for p in p_to_test.iter().cloned() {
118            let specvol = specvol(GSW_SSO, 0., p).unwrap();
119            let specvol_sso_0 = specvol_sso_0(p);
120            assert!((specvol - specvol_sso_0).abs() < f64::EPSILON);
121        }
122    }
123}
124
125pub(crate) fn enthalpy_sso_0(p: f64) -> f64 {
126    const H006: f64 = -2.107_876_881_00e-9;
127    const H007: f64 = 2.801_929_132_90e-10;
128
129    let z = p * 1.0e-4;
130
131    let dynamic_enthalpy_sso_0_p = z
132        * (9.726_613_854_843_87e-4
133            + z * (-2.252_956_605_630_465e-5
134                + z * (2.376_909_655_387_404e-6
135                    + z * (-1.664_294_869_986_011e-7
136                        + z * (-5.988_108_894_465_758e-9 + z * (H006 + H007 * z))))));
137
138    dynamic_enthalpy_sso_0_p * DB2PA * 1.0e4
139}
140
141pub(crate) fn hill_ratio_at_sp2(t: f64) -> f64 {
142    let sp2 = 2.0;
143
144    let t68: f64 = crate::conversions::t68_from_t90(t);
145    let ft68: f64 = (t68 - 15.0) / (1.0 + K * (t68 - 15.0));
146
147    // Find the initial estimates of Rtx (Rtx0) and of the derivative dSP_dRtx
148    // at SP = 2.
149    let rtx0: f64 = G0
150        + t68
151            * (G1
152                + t68
153                    * (G2
154                        + t68
155                            * (G3
156                                + t68
157                                    * (G4
158                                        + t68
159                                            * (G5
160                                                + t68
161                                                    * (G6
162                                                        + t68 * (G7 + t68 * (G8 + t68 * G9))))))));
163
164    let dsp_drtx: f64 = A1
165        + (2.0 * A2 + (3.0 * A3 + (4.0 * A4 + 5.0 * A5 * rtx0) * rtx0) * rtx0) * rtx0
166        + ft68 * (B1 + (2.0 * B2 + (3.0 * B3 + (4.0 * B4 + 5.0 * B5 * rtx0) * rtx0) * rtx0) * rtx0);
167
168    //  Begin a single modified Newton-Raphson iteration (McDougall and
169    //  Wotherspoon, 2013) to find Rt at SP = 2.
170
171    let sp_est: f64 = A0
172        + (A1 + (A2 + (A3 + (A4 + A5 * rtx0) * rtx0) * rtx0) * rtx0) * rtx0
173        + ft68 * (B0 + (B1 + (B2 + (B3 + (B4 + B5 * rtx0) * rtx0) * rtx0) * rtx0) * rtx0);
174    let rtx: f64 = rtx0 - (sp_est - sp2) / dsp_drtx;
175    let rtxm: f64 = 0.5 * (rtx + rtx0);
176    let dsp_drtx: f64 = A1
177        + (2.0 * A2 + (3.0 * A3 + (4.0 * A4 + 5.0 * A5 * rtxm) * rtxm) * rtxm) * rtxm
178        + ft68 * (B1 + (2.0 * B2 + (3.0 * B3 + (4.0 * B4 + 5.0 * B5 * rtxm) * rtxm) * rtxm) * rtxm);
179    let rtx: f64 = rtx0 - (sp_est - sp2) / dsp_drtx;
180
181    // This is the end of one full iteration of the modified Newton-Raphson
182    // iterative equation solver.  The error in Rtx at this point is
183    // equivalent to an error in SP of 9e-16 psu.
184
185    let x: f64 = 400.0 * rtx * rtx;
186    let sqrty: f64 = 10.0 * rtx;
187    let part1: f64 = 1.0 + x * (1.5 + x);
188    let part2: f64 = 1.0 + sqrty * (1.0 + sqrty * (1.0 + sqrty));
189    let sp_hill_raw_at_sp2: f64 = sp2 - A0 / part1 - B0 * ft68 / part2;
190
191    2.0 / sp_hill_raw_at_sp2
192}
193
194#[cfg(test)]
195mod test_hill_ratio_at_sp2 {
196    use super::hill_ratio_at_sp2;
197
198    #[test]
199    // These test values were obtained by running GSW-Matlab. It was common
200    // a difference of O(16), so we assumed that the values obtained with
201    // Rust were correct.
202    fn example_values() {
203        let ratio = hill_ratio_at_sp2(0.);
204        assert!((ratio - 0.999_834_364_412_733_2).abs() <= f64::EPSILON);
205
206        let ratio = hill_ratio_at_sp2(10.);
207        assert!((ratio - 0.999_958_676_175_969_7).abs() <= f64::EPSILON);
208
209        let ratio = hill_ratio_at_sp2(25.);
210        assert!((ratio - 1.000_076_483_096_847_2).abs() <= f64::EPSILON);
211
212        let ratio = hill_ratio_at_sp2(30.);
213        assert!((ratio - 1.000_104_961_868_661_5).abs() <= f64::EPSILON,);
214
215        let ratio = hill_ratio_at_sp2(40.);
216        assert!((ratio - 1.000_151_589_022_029_8).abs() <= f64::EPSILON);
217
218        let ratio = hill_ratio_at_sp2(50.);
219        assert!((ratio - 1.000_188_151_345_468_5).abs() <= f64::EPSILON);
220    }
221}
222
223#[allow(dead_code, clippy::excessive_precision, clippy::manual_range_contains)]
224/// Gibbs energy and its derivatives
225///
226/// # Notes
227/// No effort yet on optimizing, but just reproducing the results.
228pub(crate) fn gibbs(ns: u8, nt: u8, np: u8, sa: f64, t: f64, p: f64) -> Result<f64> {
229    // Temporary solution to avoid failure with powerpc64
230    if sa.is_nan() | t.is_nan() | p.is_nan() {
231        return Ok(f64::NAN);
232    }
233
234    let sa: f64 = if sa < 0.0 {
235        if cfg!(feature = "compat") {
236            0.0
237        } else if cfg!(feature = "invalidasnan") {
238            return Ok(f64::NAN);
239        } else {
240            return Err(Error::NegativeSalinity);
241        }
242    } else {
243        sa
244    };
245
246    if (sa > 120.0) | (t < -12.0) | (t > 80.0) | (p > 12_000.0) {
247        if cfg!(feature = "invalidasnan") {
248            return Ok(f64::NAN);
249        } else {
250            return Err(Error::Undefined);
251        }
252    }
253
254    let x2 = GSW_SFAC * sa;
255    let x = libm::sqrt(x2);
256    let y = t * 0.025;
257    // Note.The input pressure (p) is sea pressure in units of dbar.
258    let z = non_dimensional_p(p);
259
260    if (ns == 0) & (nt == 0) & (np == 0) {
261        let g03 = 101.342_743_139_674
262            + z * (100_015.695_367_145
263                + z * (-2_544.576_542_036_3
264                    + z * (284.517_778_446_287
265                        + z * (-33.314_675_425_361_1
266                            + (4.202_631_088_030_84 - 0.546_428_511_471_039 * z) * z))))
267            + y * (5.905_783_479_094_02
268                + z * (-270.983_805_184_062
269                    + z * (776.153_611_613_101
270                        + z * (-196.512_550_881_22
271                            + (28.979_652_629_417_5 - 2.132_900_835_183_27 * z) * z)))
272                + y * (-12_357.785_933_039
273                    + z * (1_455.036_454_046_8
274                        + z * (-756.558_385_769_359
275                            + z * (273.479_662_323_528
276                                + z * (-55.560_406_381_721_8 + 4.344_206_719_171_97 * z))))
277                    + y * (736.741_204_151_612
278                        + z * (-672.507_783_145_07
279                            + z * (499.360_390_819_152
280                                + z * (-239.545_330_654_412
281                                    + (48.801_251_859_387_2 - 1.663_071_062_089_05 * z) * z)))
282                        + y * (-148.185_936_433_658
283                            + z * (397.968_445_406_972
284                                + z * (-301.815_380_621_876
285                                    + (152.196_371_733_841 - 26.374_837_723_280_2 * z) * z))
286                            + y * (58.025_912_584_257_1
287                                + z * (-194.618_310_617_595
288                                    + z * (120.520_654_902_025
289                                        + z * (-55.272_305_234_015_2
290                                            + 6.481_906_680_772_21 * z)))
291                                + y * (-18.984_384_651_417_2
292                                    + y * (3.050_816_464_879_67 - 9.631_081_193_930_62 * z)
293                                    + z * (63.511_393_664_178_5
294                                        + z * (-22.289_731_714_045_9
295                                            + 8.170_605_418_181_12 * z))))))));
296
297        let mut g08 = x2
298            * (1_416.276_484_841_97
299                + z * (-3_310.491_540_448_39
300                    + z * (384.794_152_978_599
301                        + z * (-96.532_432_010_745_8
302                            + (15.840_817_276_682_4 - 2.624_801_565_909_92 * z) * z)))
303                + x * (-2_432.146_623_817_94
304                    + x * (2_025.801_156_036_97
305                        + y * (543.835_333_000_098
306                            + y * (-68.557_250_920_449_1
307                                + y * (49.366_769_485_625_4
308                                    + y * (-17.139_757_741_978_8 + 2.496_970_095_695_08 * y)))
309                            - 22.668_355_851_282_9 * z)
310                        + x * (-1_091.668_410_429_67 - 196.028_306_689_776 * y
311                            + x * (374.601_237_877_84 - 48.589_106_902_540_9 * x
312                                + 36.757_162_299_580_5 * y)
313                            + 36.028_419_561_108_6 * z)
314                        + z * (-54.791_913_353_288_7
315                            + (-4.081_939_789_122_61 - 30.175_511_197_116_1 * z) * z))
316                    + z * (199.459_603_073_901
317                        + z * (-52.294_090_928_133_5
318                            + (68.044_494_272_645_9 - 3.412_519_324_412_82 * z) * z))
319                    + y * (-493.407_510_141_682
320                        + z * (-175.292_041_186_547
321                            + (83.192_392_780_181_9 - 29.483_064_349_429 * z) * z)
322                        + y * (-43.066_467_597_804_2
323                            + z * (383.058_066_002_476
324                                + z * (-54.191_726_251_711_2 + 25.639_848_738_991_4 * z))
325                            + y * (-10.022_737_086_187_5 - 460.319_931_801_257 * z
326                                + y * (0.875_600_661_808_945 + 234.565_187_611_355 * z)))))
327                + y * (168.072_408_311_545
328                    + z * (729.116_529_735_046
329                        + z * (-343.956_902_961_561
330                            + z * (124.687_671_116_248
331                                + z * (-31.656_964_386_073 + 7.046_588_033_154_49 * z))))
332                    + y * (880.031_352_997_204
333                        + y * (-225.267_649_263_401
334                            + y * (91.426_044_775_125_9
335                                + y * (-21.660_324_087_531_1 + 2.130_169_708_471_83 * y)
336                                + z * (-297.728_741_987_187
337                                    + (74.726_141_138_756 - 36.487_291_900_158_8 * z) * z))
338                            + z * (694.244_814_133_268
339                                + z * (-204.889_641_964_903
340                                    + (113.561_697_840_594 - 11.128_273_432_641_3 * z) * z)))
341                        + z * (-860.764_303_783_977
342                            + z * (337.409_530_269_367
343                                + z * (-178.314_556_207_638
344                                    + (44.204_035_830_8 - 7.920_015_472_116_82 * z) * z))))));
345
346        if x > 0.0 {
347            g08 += x2 * (5_812.814_566_267_32 + 851.226_734_946_706 * y) * libm::log(x);
348        }
349
350        Ok(g03 + g08)
351    } else if (ns == 1) & (nt == 0) & (np == 0) {
352        let mut g08 = 8_645.367_535_951_26
353            + z * (-6_620.983_080_896_78
354                + z * (769.588_305_957_198
355                    + z * (-193.064_864_021_491_6
356                        + (31.681_634_553_364_8 - 5.249_603_131_819_84 * z) * z)))
357            + x * (-7_296.439_871_453_82
358                + x * (8_103.204_624_147_88
359                    + y * (2_175.341_332_000_392
360                        + y * (-274.229_003_681_796_4
361                            + y * (197.467_077_942_501_6
362                                + y * (-68.559_030_967_915_2 + 9.987_880_382_780_32 * y)))
363                        - 90.673_423_405_131_6 * z)
364                    + x * (-5_458.342_052_148_35 - 980.141_533_448_88 * y
365                        + x * (2_247.607_427_267_04 - 340.123_748_317_786_3 * x
366                            + 220.542_973_797_483 * y)
367                        + 180.142_097_805_543 * z)
368                    + z * (-219.167_653_413_154_8
369                        + (-16.327_759_156_490_44 - 120.702_044_788_464_4 * z) * z))
370                + z * (598.378_809_221_703
371                    + z * (-156.882_272_784_400_5
372                        + (204.133_482_817_937_7 - 10.237_557_973_238_46 * z) * z))
373                + y * (-1_480.222_530_425_046
374                    + z * (-525.876_123_559_641
375                        + (249.577_178_340_545_71 - 88.449_193_048_287 * z) * z)
376                    + y * (-129.199_402_793_412_6
377                        + z * (1_149.174_198_007_428
378                            + z * (-162.575_178_755_133_6 + 76.919_546_216_974_2 * z))
379                        + y * (-30.068_211_258_562_5 - 1_380.959_795_403_770_8 * z
380                            + y * (2.626_801_985_426_835 + 703.695_562_834_065 * z)))))
381            + y * (1_187.371_551_569_795_9
382                + z * (1_458.233_059_470_092
383                    + z * (-687.913_805_923_122
384                        + z * (249.375_342_232_496
385                            + z * (-63.313_928_772_146 + 14.093_176_066_308_98 * z))))
386                + y * (1_760.062_705_994_408
387                    + y * (-450.535_298_526_802
388                        + y * (182.852_089_550_251_8
389                            + y * (-43.320_648_175_062_2 + 4.260_339_416_943_66 * y)
390                            + z * (-595.457_483_974_374
391                                + (149.452_282_277_512 - 72.974_583_800_317_6 * z) * z))
392                        + z * (1_388.489_628_266_536
393                            + z * (-409.779_283_929_806
394                                + (227.123_395_681_188 - 22.256_546_865_282_6 * z) * z)))
395                    + z * (-1_721.528_607_567_954
396                        + z * (674.819_060_538_734
397                            + z * (-356.629_112_415_276
398                                + (88.408_071_661_6 - 15.840_030_944_233_64 * z) * z)))));
399
400        if x > 0.0 {
401            g08 += (11_625.629_132_534_64 + 1_702.453_469_893_412 * y) * libm::log(x);
402        }
403        if x == 0.0 {
404            g08 = f64::NAN;
405        }
406
407        Ok(0.5 * GSW_SFAC * g08)
408    } else if (ns == 0) & (nt == 1) & (np == 0) {
409        let g03 = 5.905_783_479_094_02
410            + z * (-270.983_805_184_062
411                + z * (776.153_611_613_101
412                    + z * (-196.512_550_881_22
413                        + (28.979_652_629_417_5 - 2.132_900_835_183_27 * z) * z)))
414            + y * (-24_715.571_866_078
415                + z * (2_910.072_908_093_6
416                    + z * (-1_513.116_771_538_718
417                        + z * (546.959_324_647_056
418                            + z * (-111.120_812_763_443_6 + 8.688_413_438_343_94 * z))))
419                + y * (2_210.223_612_454_836_3
420                    + z * (-2_017.523_349_435_21
421                        + z * (1_498.081_172_457_456
422                            + z * (-718.635_991_963_235_9
423                                + (146.403_755_578_161_6 - 4.989_213_186_267_150_5 * z) * z)))
424                    + y * (-592.743_745_734_632
425                        + z * (1_591.873_781_627_888
426                            + z * (-1_207.261_522_487_504
427                                + (608.785_486_935_364 - 105.499_350_893_120_8 * z) * z))
428                        + y * (290.129_562_921_285_47
429                            + z * (-973.091_553_087_975
430                                + z * (602.603_274_510_125
431                                    + z * (-276.361_526_170_076 + 32.409_533_403_861_05 * z)))
432                            + y * (-113.906_307_908_503_21
433                                + y * (21.355_715_254_157_69 - 67.417_568_357_514_34 * z)
434                                + z * (381.068_361_985_070_96
435                                    + z * (-133.738_390_284_275_4
436                                        + 49.023_632_509_086_724 * z)))))));
437
438        let mut g08 = x2
439            * (168.072_408_311_545
440                + z * (729.116_529_735_046
441                    + z * (-343.956_902_961_561
442                        + z * (124.687_671_116_248
443                            + z * (-31.656_964_386_073 + 7.046_588_033_154_49 * z))))
444                + x * (-493.407_510_141_682
445                    + x * (543.835_333_000_098
446                        + x * (-196.028_306_689_776 + 36.757_162_299_580_5 * x)
447                        + y * (-137.114_501_840_898_2
448                            + y * (148.100_308_456_876_18
449                                + y * (-68.559_030_967_915_2 + 12.484_850_478_475_4 * y)))
450                        - 22.668_355_851_282_9 * z)
451                    + z * (-175.292_041_186_547
452                        + (83.192_392_780_181_9 - 29.483_064_349_429 * z) * z)
453                    + y * (-86.132_935_195_608_4
454                        + z * (766.116_132_004_952
455                            + z * (-108.383_452_503_422_4 + 51.279_697_477_982_8 * z))
456                        + y * (-30.068_211_258_562_5 - 1_380.959_795_403_770_8 * z
457                            + y * (3.502_402_647_235_78 + 938.260_750_445_42 * z))))
458                + y * (1_760.062_705_994_408
459                    + y * (-675.802_947_790_203
460                        + y * (365.704_179_100_503_6
461                            + y * (-108.301_620_437_655_52 + 12.781_018_250_830_98 * y)
462                            + z * (-1_190.914_967_948_748
463                                + (298.904_564_555_024 - 145.949_167_600_635_2 * z) * z))
464                        + z * (2_082.734_442_399_804_3
465                            + z * (-614.668_925_894_709
466                                + (340.685_093_521_782 - 33.384_820_297_923_9 * z) * z)))
467                    + z * (-1_721.528_607_567_954
468                        + z * (674.819_060_538_734
469                            + z * (-356.629_112_415_276
470                                + (88.408_071_661_6 - 15.840_030_944_233_64 * z) * z)))));
471
472        if x > 0.0 {
473            g08 += 851.226_734_946_706 * x2 * libm::log(x);
474        }
475
476        Ok((g03 + g08) * 0.025)
477    } else if (ns == 0) & (nt == 0) & (np == 1) {
478        let g03 = 100_015.695_367_145
479            + z * (-5_089.153_084_072_6
480                + z * (853.553_335_338_861_1
481                    + z * (-133.258_701_701_444_4
482                        + (21.013_155_440_154_2 - 3.278_571_068_826_234 * z) * z)))
483            + y * (-270.983_805_184_062
484                + z * (1_552.307_223_226_202
485                    + z * (-589.537_652_643_66
486                        + (115.918_610_517_67 - 10.664_504_175_916_349 * z) * z))
487                + y * (1_455.036_454_046_8
488                    + z * (-1_513.116_771_538_718
489                        + z * (820.438_986_970_584
490                            + z * (-222.241_625_526_887_2 + 21.721_033_595_859_85 * z)))
491                    + y * (-672.507_783_145_07
492                        + z * (998.720_781_638_304
493                            + z * (-718.635_991_963_235_9
494                                + (195.205_007_437_548_8 - 8.315_355_310_445_25 * z) * z))
495                        + y * (397.968_445_406_972
496                            + z * (-603.630_761_243_752
497                                + (456.589_115_201_523 - 105.499_350_893_120_8 * z) * z)
498                            + y * (-194.618_310_617_595
499                                + y * (63.511_393_664_178_5 - 9.631_081_193_930_62 * y
500                                    + z * (-44.579_463_428_091_8
501                                        + 24.511_816_254_543_362 * z))
502                                + z * (241.041_309_804_05
503                                    + z * (-165.816_915_702_045_6
504                                        + 25.927_626_723_088_84 * z)))))));
505
506        let g08 = x2
507            * (-3_310.491_540_448_39
508                + z * (769.588_305_957_198
509                    + z * (-289.597_296_032_237_4
510                        + (63.363_269_106_729_6 - 13.124_007_829_549_6 * z) * z))
511                + x * (199.459_603_073_901
512                    + x * (-54.791_913_353_288_7 + 36.028_419_561_108_6 * x
513                        - 22.668_355_851_282_9 * y
514                        + (-8.163_879_578_245_22 - 90.526_533_591_348_31 * z) * z)
515                    + z * (-104.588_181_856_267
516                        + (204.133_482_817_937_7 - 13.650_077_297_651_28 * z) * z)
517                    + y * (-175.292_041_186_547
518                        + (166.384_785_560_363_8 - 88.449_193_048_287 * z) * z
519                        + y * (383.058_066_002_476
520                            + y * (-460.319_931_801_257 + 234.565_187_611_355 * y)
521                            + z * (-108.383_452_503_422_4 + 76.919_546_216_974_2 * z))))
522                + y * (729.116_529_735_046
523                    + z * (-687.913_805_923_122
524                        + z * (374.063_013_348_744
525                            + z * (-126.627_857_544_292 + 35.232_940_165_772_45 * z)))
526                    + y * (-860.764_303_783_977
527                        + y * (694.244_814_133_268
528                            + y * (-297.728_741_987_187
529                                + (149.452_282_277_512 - 109.461_875_700_476_41 * z) * z)
530                            + z * (-409.779_283_929_806
531                                + (340.685_093_521_782 - 44.513_093_730_565_2 * z) * z))
532                        + z * (674.819_060_538_734
533                            + z * (-534.943_668_622_914
534                                + (176.816_143_323_2 - 39.600_077_360_584_095 * z) * z)))));
535        Ok((g03 + g08) * 1e-8)
536    //% Note. This pressure derivative of the gibbs function is in units of (J/kg) (Pa^-1) = m^3/kg
537    } else if (ns == 1) & (nt == 1) & (np == 0) {
538        let mut g08 = 1_187.371_551_569_795_9
539            + z * (1_458.233_059_470_092
540                + z * (-687.913_805_923_122
541                    + z * (249.375_342_232_496
542                        + z * (-63.313_928_772_146 + 14.093_176_066_308_98 * z))))
543            + x * (-1_480.222_530_425_046
544                + x * (2_175.341_332_000_392
545                    + x * (-980.141_533_448_88 + 220.542_973_797_483 * x)
546                    + y * (-548.458_007_363_592_9
547                        + y * (592.401_233_827_504_7
548                            + y * (-274.236_123_871_660_8 + 49.939_401_913_901_6 * y)))
549                    - 90.673_423_405_131_6 * z)
550                + z * (-525.876_123_559_641
551                    + (249.577_178_340_545_71 - 88.449_193_048_287 * z) * z)
552                + y * (-258.398_805_586_825_2
553                    + z * (2_298.348_396_014_856
554                        + z * (-325.150_357_510_267_2 + 153.839_092_433_948_4 * z))
555                    + y * (-90.204_633_775_687_5 - 4_142.879_386_211_312_5 * z
556                        + y * (10.507_207_941_707_34 + 2_814.782_251_336_26 * z))))
557            + y * (3_520.125_411_988_816
558                + y * (-1_351.605_895_580_406
559                    + y * (731.408_358_201_007_2
560                        + y * (-216.603_240_875_311_03 + 25.562_036_501_661_96 * y)
561                        + z * (-2_381.829_935_897_496
562                            + (597.809_129_110_048 - 291.898_335_201_270_4 * z) * z))
563                    + z * (4_165.468_884_799_608_5
564                        + z * (-1_229.337_851_789_418
565                            + (681.370_187_043_564 - 66.769_640_595_847_8 * z) * z)))
566                + z * (-3_443.057_215_135_908
567                    + z * (1_349.638_121_077_468
568                        + z * (-713.258_224_830_552
569                            + (176.816_143_323_2 - 31.680_061_888_467_28 * z) * z))));
570
571        if sa == 0.0 {
572            g08 = f64::NAN;
573        } else if x > 0.0 {
574            g08 += 1_702.453_469_893_412 * libm::log(x);
575        }
576
577        Ok(0.5 * GSW_SFAC * 0.025 * g08)
578    } else if (ns == 1) & (nt == 0) & (np == 1) {
579        let g08 = -6_620.983_080_896_78
580            + z * (1_539.176_611_914_396
581                + z * (-579.194_592_064_474_8
582                    + (126.726_538_213_459_2 - 26.248_015_659_099_2 * z) * z))
583            + x * (598.378_809_221_703
584                + x * (-219.167_653_413_154_8 + 180.142_097_805_543 * x
585                    - 90.673_423_405_131_6 * y
586                    + (-32.655_518_312_980_88 - 362.106_134_365_393_25 * z) * z)
587                + z * (-313.764_545_568_801
588                    + (612.400_448_453_813_2 - 40.950_231_892_953_84 * z) * z)
589                + y * (-525.876_123_559_641
590                    + (499.154_356_681_091_43 - 265.347_579_144_861 * z) * z
591                    + y * (1_149.174_198_007_428
592                        + y * (-1_380.959_795_403_770_8 + 703.695_562_834_065 * y)
593                        + z * (-325.150_357_510_267_2 + 230.758_638_650_922_6 * z))))
594            + y * (1_458.233_059_470_092
595                + z * (-1_375.827_611_846_244
596                    + z * (748.126_026_697_488
597                        + z * (-253.255_715_088_584 + 70.465_880_331_544_9 * z)))
598                + y * (-1_721.528_607_567_954
599                    + y * (1_388.489_628_266_536
600                        + y * (-595.457_483_974_374
601                            + (298.904_564_555_024 - 218.923_751_400_952_82 * z) * z)
602                        + z * (-819.558_567_859_612
603                            + (681.370_187_043_564 - 89.026_187_461_130_4 * z) * z))
604                    + z * (1_349.638_121_077_468
605                        + z * (-1_069.887_337_245_828
606                            + (353.632_286_646_4 - 79.200_154_721_168_19 * z) * z))));
607
608        Ok(g08 * GSW_SFAC * 0.5e-8)
609    // Note. This derivative of the Gibbs function is in units of (m^3/kg)/(g/kg) = m^3/g,
610    // that is, it is the derivative of specific volume with respect to Absolute
611    // Salinity measured in g/kg.
612    } else if (ns == 0) & (nt == 1) & (np == 1) {
613        let g03 = -270.983_805_184_062
614            + z * (1_552.307_223_226_202
615                + z * (-589.537_652_643_66
616                    + (115.918_610_517_67 - 10.664_504_175_916_349 * z) * z))
617            + y * (2_910.072_908_093_6
618                + z * (-3_026.233_543_077_436
619                    + z * (1_640.877_973_941_168
620                        + z * (-444.483_251_053_774_4 + 43.442_067_191_719_7 * z)))
621                + y * (-2_017.523_349_435_21
622                    + z * (2_996.162_344_914_912
623                        + z * (-2_155.907_975_889_708
624                            + (585.615_022_312_646_4 - 24.946_065_931_335_752 * z) * z))
625                    + y * (1_591.873_781_627_888
626                        + z * (-2_414.523_044_975_008
627                            + (1_826.356_460_806_092 - 421.997_403_572_483_2 * z) * z)
628                        + y * (-973.091_553_087_975
629                            + z * (1_205.206_549_020_25
630                                + z * (-829.084_578_510_228 + 129.638_133_615_444_2 * z))
631                            + y * (381.068_361_985_070_96 - 67.417_568_357_514_34 * y
632                                + z * (-267.476_780_568_550_8 + 147.070_897_527_260_17 * z))))));
633
634        let g08 = x2
635            * (729.116_529_735_046
636                + z * (-687.913_805_923_122
637                    + z * (374.063_013_348_744
638                        + z * (-126.627_857_544_292 + 35.232_940_165_772_45 * z)))
639                + x * (-175.292_041_186_547 - 22.668_355_851_282_9 * x
640                    + (166.384_785_560_363_8 - 88.449_193_048_287 * z) * z
641                    + y * (766.116_132_004_952
642                        + y * (-1_380.959_795_403_770_8 + 938.260_750_445_42 * y)
643                        + z * (-216.766_905_006_844_8 + 153.839_092_433_948_4 * z)))
644                + y * (-1_721.528_607_567_954
645                    + y * (2_082.734_442_399_804_3
646                        + y * (-1_190.914_967_948_748
647                            + (597.809_129_110_048 - 437.847_502_801_905_65 * z) * z)
648                        + z * (-1_229.337_851_789_418
649                            + (1_022.055_280_565_346 - 133.539_281_191_695_6 * z) * z))
650                    + z * (1_349.638_121_077_468
651                        + z * (-1_069.887_337_245_828
652                            + (353.632_286_646_4 - 79.200_154_721_168_19 * z) * z))));
653
654        Ok((g03 + g08) * 2.5e-10)
655    // Note. This derivative of the Gibbs function is in units of (m^3/(K kg)),
656    // that is, the pressure of the derivative in Pa.
657    } else if (ns == 2) & (nt == 0) & (np == 0) {
658        let mut g08 = 5_812.814_566_267_320
659            + 851.226_734_946_706_0 * y
660            + x * (-3_648.219_935_726_910
661                + x * (8_103.204_624_147_880
662                    + x * (-8_187.513_078_222_526
663                        + x * (4_495.214_854_534_080 - 850.309_370_794_465_7 * x)))
664                + y * (-740.111_265_212_523_0
665                    + x * (2_175.341_332_000_392
666                        + x * (-1_470.212_300_173_320 + 441.085_947_594_966_0 * x))
667                    + y * (-64.599_701_396_706_29 - 274.229_003_681_796_4 * x
668                        + y * (-15.034_105_629_281_25
669                            + 197.467_077_942_501_6 * x
670                            + y * (1.313_400_992_713_418 - 68.559_030_967_915_21 * x
671                                + 9.987_880_382_780_312 * x * y))))
672                + z * (299.189_404_610_851_5
673                    + x * (-219.167_653_413_154_8 + 270.213_146_708_314_5 * x)
674                    + y * (-262.938_061_779_820_5 - 90.673_423_405_131_60 * x
675                        + y * (574.587_099_003_714_0
676                            + y * (-690.479_897_701_885_4 + 351.847_781_417_032_5 * y)))
677                    + z * (-78.441_136_392_200_25 - 16.327_759_156_490_44 * x
678                        + y * (124.788_589_170_272_9 - 81.287_589_377_566_80 * y)
679                        + z * (102.066_741_408_968_9 - 120.702_044_788_464_4 * x
680                            + y * (-44.224_596_524_143_50 + 38.459_773_108_487_10 * y)
681                            - 5.118_778_986_619_230 * z))));
682
683        if x > 0.0 {
684            g08 /= x2;
685        }
686        if x == 0.0 {
687            g08 = f64::NAN;
688        }
689
690        Ok(0.5 * GSW_SFAC * GSW_SFAC * g08)
691    } else if (ns == 0) & (nt == 2) & (np == 0) {
692        let g03 = -24_715.571_866_078
693            + z * (2_910.072_908_093_6
694                + z * (-1_513.116_771_538_718
695                    + z * (546.959_324_647_056
696                        + z * (-111.120_812_763_443_6 + 8.688_413_438_343_94 * z))))
697            + y * (4_420.447_224_909_672_5
698                + z * (-4_035.046_698_870_42
699                    + z * (2_996.162_344_914_912
700                        + z * (-1_437.271_983_926_471_9
701                            + (292.807_511_156_323_2 - 9.978_426_372_534_301 * z) * z)))
702                + y * (-1_778.231_237_203_896
703                    + z * (4_775.621_344_883_664
704                        + z * (-3_621.784_567_462_512
705                            + (1_826.356_460_806_092 - 316.498_052_679_362_44 * z) * z))
706                    + y * (1_160.518_251_685_141_9
707                        + z * (-3_892.366_212_351_9
708                            + z * (2_410.413_098_040_5
709                                + z * (-1_105.446_104_680_304 + 129.638_133_615_444_2 * z)))
710                        + y * (-569.531_539_542_516
711                            + y * (128.134_291_524_946_15 - 404.505_410_145_086_05 * z)
712                            + z * (1_905.341_809_925_355
713                                + z * (-668.691_951_421_377 + 245.118_162_545_433_62 * z))))));
714
715        let g08 = x2
716            * (1_760.062_705_994_408
717                + x * (-86.132_935_195_608_4
718                    + x * (-137.114_501_840_898_2
719                        + y * (296.200_616_913_752_36
720                            + y * (-205.677_092_903_745_63 + 49.939_401_913_901_6 * y)))
721                    + z * (766.116_132_004_952
722                        + z * (-108.383_452_503_422_4 + 51.279_697_477_982_8 * z))
723                    + y * (-60.136_422_517_125 - 2_761.919_590_807_541_7 * z
724                        + y * (10.507_207_941_707_34 + 2_814.782_251_336_26 * z)))
725                + y * (-1_351.605_895_580_406
726                    + y * (1_097.112_537_301_510_9
727                        + y * (-433.206_481_750_622_06 + 63.905_091_254_154_904 * y)
728                        + z * (-3_572.744_903_846_243_7
729                            + (896.713_693_665_072 - 437.847_502_801_905_65 * z) * z))
730                    + z * (4_165.468_884_799_608_5
731                        + z * (-1_229.337_851_789_418
732                            + (681.370_187_043_564 - 66.769_640_595_847_8 * z) * z)))
733                + z * (-1_721.528_607_567_954
734                    + z * (674.819_060_538_734
735                        + z * (-356.629_112_415_276
736                            + (88.408_071_661_6 - 15.840_030_944_233_64 * z) * z))));
737
738        Ok((g03 + g08) * 0.000_625)
739    } else if (ns == 0) & (nt == 0) & (np == 2) {
740        let g03 = -5_089.153_084_072_6
741            + z * (1_707.106_670_677_722_1
742                + z * (-399.776_105_104_333_2
743                    + (84.052_621_760_616_8 - 16.392_855_344_131_17 * z) * z))
744            + y * (1_552.307_223_226_202
745                + z * (-1_179.075_305_287_32
746                    + (347.755_831_553_01 - 42.658_016_703_665_396 * z) * z)
747                + y * (-1_513.116_771_538_718
748                    + z * (1_640.877_973_941_168
749                        + z * (-666.724_876_580_661_5 + 86.884_134_383_439_4 * z))
750                    + y * (998.720_781_638_304
751                        + z * (-1_437.271_983_926_471_9
752                            + (585.615_022_312_646_4 - 33.261_421_241_781 * z) * z)
753                        + y * (-603.630_761_243_752
754                            + (913.178_230_403_046 - 316.498_052_679_362_44 * z) * z
755                            + y * (241.041_309_804_05
756                                + y * (-44.579_463_428_091_8 + 49.023_632_509_086_724 * z)
757                                + z * (-331.633_831_404_091_2 + 77.782_880_169_266_52 * z))))));
758
759        let g08 = x2
760            * (769.588_305_957_198
761                + z * (-579.194_592_064_474_8
762                    + (190.089_807_320_188_78 - 52.496_031_318_198_4 * z) * z)
763                + x * (-104.588_181_856_267
764                    + x * (-8.163_879_578_245_22 - 181.053_067_182_696_62 * z)
765                    + (408.266_965_635_875_4 - 40.950_231_892_953_84 * z) * z
766                    + y * (166.384_785_560_363_8 - 176.898_386_096_574 * z
767                        + y * (-108.383_452_503_422_4 + 153.839_092_433_948_4 * z)))
768                + y * (-687.913_805_923_122
769                    + z * (748.126_026_697_488
770                        + z * (-379.883_572_632_876 + 140.931_760_663_089_8 * z))
771                    + y * (674.819_060_538_734
772                        + z * (-1_069.887_337_245_828
773                            + (530.448_429_969_6 - 158.400_309_442_336_38 * z) * z)
774                        + y * (-409.779_283_929_806
775                            + y * (149.452_282_277_512 - 218.923_751_400_952_82 * z)
776                            + (681.370_187_043_564 - 133.539_281_191_695_6 * z) * z))));
777
778        Ok((g03 + g08) * 1e-16)
779    // Note. This is the second derivative of the Gibbs function with respect to
780    //pressure, measured in Pa.  This derivative has units of (J/kg) (Pa^-2).
781    } else if (ns == 0) & (nt == 3) & (np == 0) {
782        let g03 = 4_420.447_224_909_672_51
783            + y * (-3_556.462_474_407_791_89
784                + y * (3_481.554_755_055_425_63
785                    + y * (-2_278.126_158_170_064_17 + 640.671_457_624_730_749 * y)))
786            + z * (-4_035.046_698_870_420_19
787                + y * (9_551.242_689_767_328_56
788                    + y * (-11_677.098_637_055_699_3
789                        + y * (7_621.367_239_701_419_75 - 2_022.527_050_725_430_23 * y)))
790                + z * (2_996.162_344_914_911_96
791                    + y * (-7_243.569_134_925_023_72
792                        + y * (7_231.239_294_121_499_37 - 2_674.767_805_685_507_94 * y))
793                    + z * (-1_437.271_983_926_471_88
794                        + y * (3_652.712_921_612_183_89
795                            + y * (-3_316.338_314_040_911_88 + 980.472_650_181_734_480 * y))
796                        + z * (292.807_511_156_323_187
797                            + y * (-632.996_105_358_724_890 + 388.914_400_846_332_569 * y)
798                            - 9.978_426_372_534_300_98 * z))));
799
800        let g08 = x2
801            * (-1_351.605_895_580_406
802                + x * (-60.136_422_517_125_0 + 296.200_616_913_752 * x)
803                + y * (2_194.225_074_603_02
804                    + y * (-1_299.619_445_251_87 + 255.620_365_016_620 * y)
805                    + x * (21.014_415_883_414_7
806                        + x * (-411.354_185_807_491 + 149.818_205_741_705 * y)))
807                + z * (4_165.468_884_799_61 - 2_761.919_590_807_54 * x
808                    + y * (-7_145.489_807_692_49 + 5_629.564_502_672_52 * x)
809                    + z * (-1_229.337_851_789_42
810                        + 1_793.427_387_330_14 * y
811                        + z * (681.370_187_043_564
812                            - 875.695_005_603_811 * y
813                            - 66.769_640_595_847_8 * z))));
814
815        Ok((g03 + g08) * 0.000_625 * 0.025)
816    } else if (ns == 0) & (nt == 2) & (np == 1) {
817        let g03 = 2_910.072_908_093_60
818            + y * (-4_035.046_698_870_42
819                + y * (4_775.621_344_883_66
820                    + y * (-3_892.366_212_351_90
821                        + y * (1_905.341_809_925_35 - 404.505_410_145_086 * y))))
822            + z * (-3_026.233_543_077_44
823                + y * (5_992.324_689_829_82
824                    + y * (-7_243.569_134_925_02
825                        + y * (4_820.826_196_081_00 - 1_337.383_902_842_75 * y)))
826                + z * (1_640.877_973_941_17
827                    + y * (-4_311.815_951_779_42
828                        + y * (5_479.069_382_418_28
829                            + y * (-3_316.338_314_040_91 + 735.354_487_636_301 * y)))
830                    + z * (-444.483_251_053_774
831                        + y * (1_171.230_044_625_29
832                            + y * (-1_265.992_210_717_45 + 518.552_534_461_777 * y))
833                        + z * (43.442_067_191_719_7 - 49.892_131_862_671_5 * y))));
834
835        let g08 = x2
836            * (-1_721.528_607_567_95 + 766.116_132_004_952 * x - 2_761.919_590_807_54 * x * y
837                + y * (4_165.468_884_799_61
838                    + y * (-3_572.744_903_846_24 + 2_814.782_251_336_26 * x))
839                + z * (1_349.638_121_077_47 - 216.766_905_006_845 * x
840                    + y * (-2_458.675_703_578_84 + 1_793.427_387_330_14 * y)
841                    + z * (-1_069.887_337_245_83
842                        + 153.839_092_433_949 * x
843                        + y * (2_044.110_561_130_69 - 1_313.542_508_405_72 * y)
844                        + z * (353.632_286_646_400
845                            - 267.078_562_383_391 * y
846                            - 79.200_154_721_168_2 * z))));
847
848        Ok((g03 + g08) * 2.5e-10 * 0.025)
849    } else if (ns == 1) & (nt == 1) & (np == 1) {
850        let g08 = 1_458.233_059_470_09
851            + x * (-525.876_123_559_641 - 90.673_423_405_131_6 * x
852                + y * (2_298.348_396_014_86
853                    + y * (-4_142.879_386_211_31 + 2_814.782_251_336_26 * y)))
854            + y * (-3_443.057_215_135_91 + y * (4_165.468_884_799_61 - 2_381.829_935_897_50 * y))
855            + z * (-1_375.827_611_846_24
856                + x * (499.154_356_681_091 - 650.300_715_020_534 * y)
857                + y * (2_699.276_242_154_94
858                    + y * (-2_458.675_703_578_84 + 1_195.618_258_220_10 * y))
859                + z * (748.126_026_697_488
860                    + x * (-265.347_579_144_861 + 461.517_277_301_845 * y)
861                    + y * (-2_139.774_674_491_66
862                        + y * (2_044.110_561_130_69 - 875.695_005_603_811 * y))
863                    + z * (-253.255_715_088_584
864                        + y * (707.264_573_292_800 - 267.078_562_383_391 * y)
865                        + z * (70.465_880_331_544_9 - 158.400_309_442_336 * y))));
866
867        Ok(g08 * GSW_SFAC * 0.5e-8 * 0.025)
868    } else if (ns == 2) & (nt == 0) & (np == 1) {
869        let mut g08 = 299.189_404_610_851_5
870            + x * (-219.167_653_413_154_8 + 270.213_146_708_314_5 * x)
871            + y * (-262.938_061_779_820_5 - 90.673_423_405_131_60 * x
872                + y * (574.587_099_003_714_0
873                    + y * (-690.479_897_701_885_4 + 351.847_781_417_032_5 * y)))
874            + z * (-156.882_272_784_400_5 - 32.655_518_312_980_88 * x
875                + y * (249.577_178_340_545_7 - 162.575_178_755_133_6 * y)
876                + z * (306.200_224_226_906_6 - 362.106_134_365_393_2 * x
877                    + y * (-132.673_789_572_430_5 + 115.379_319_325_461_3 * y)
878                    - 20.475_115_946_476_92 * z));
879
880        if x > 0.0 {
881            g08 /= x;
882        }
883        if x == 0.0 {
884            g08 = f64::NAN;
885        }
886
887        Ok(0.5 * GSW_SFAC * GSW_SFAC * g08 * 1e-8)
888    } else if (ns == 1) & (nt == 2) & (np == 0) {
889        let mut g08 = 3_520.125_411_988_816
890            + x * (-258.398_805_586_825_2 - 548.458_007_363_592_9 * x)
891            + y * (-2_703.211_791_160_812
892                + x * (-180.409_267_551_375 + 1_184.802_467_655_009_4 * x)
893                + y * (2_194.225_074_603_021_7
894                    + x * (31.521_623_825_122_02
895                        + x * (-822.708_371_614_982_47 + 199.757_607_655_606_4 * y))
896                    + y * (-866.412_963_501_244_1 + 127.810_182_508_309_8 * y)))
897            + z * (-3_443.057_215_135_908
898                + 2_298.348_396_014_856 * x
899                + y * (8_330.937_769_599_217 - 8_285.758_772_422_625 * x
900                    + y * (-7_145.489_807_692_487_8 + 8_444.346_754_008_78 * x))
901                + z * (1_349.638_121_077_468 - 325.150_357_510_267_2 * x
902                    + y * (-2_458.675_703_578_836 + 1_793.427_387_330_144 * y)
903                    + z * (-713.258_224_830_552
904                        + 153.839_092_433_948_4 * x
905                        + y * (1_362.740_374_087_128 - 875.695_005_603_811_2 * y)
906                        + z * (176.816_143_323_2
907                            - 133.539_281_191_695_6 * y
908                            - 31.680_061_888_467_3 * z))));
909
910        if x == 0.0 {
911            g08 = f64::NAN;
912        }
913
914        Ok(0.5 * GSW_SFAC * 0.025 * g08 * 0.025)
915    } else if (ns == 2) & (nt == 1) & (np == 0) {
916        let mut g08 = 851.226_734_946_705_96
917            + x * (-740.111_265_212_522_99
918                + x * (2_175.341_332_000_392_0
919                    + x * (-1_470.212_300_173_319_9 + 441.085_947_594_966_00 * x))
920                + y * (-129.199_402_793_412_59 - 548.458_007_363_592_86 * x
921                    + y * (-45.102_316_887_843_749
922                        + 592.401_233_827_504_77 * x
923                        + y * (5.253_603_970_853_670_4
924                            + x * (-274.236_123_871_660_82 + 49.939_401_913_901_600 * y))))
925                + z * (-262.938_061_779_820_49 - 90.673_423_405_131_601 * x
926                    + y * (1_149.174_198_007_428_0
927                        + y * (-2_071.439_693_105_656_3 + 1_407.391_125_668_129_9 * y))
928                    + z * (124.788_589_170_272_86 - 162.575_178_755_133_61 * y
929                        + z * (-44.224_596_524_143_500 + 76.919_546_216_974_197 * y))));
930
931        if x > 0.0 {
932            g08 /= x2;
933        }
934        if x == 0.0 {
935            g08 = f64::NAN;
936        }
937
938        Ok(0.5 * GSW_SFAC * GSW_SFAC * g08 * 0.025)
939    } else if (ns == 1) & (nt == 0) & (np == 2) {
940        let g08 = 1_539.176_611_914_396_06
941            + x * (-313.764_545_568_801 - 32.655_518_312_980_9 * x)
942            + y * (-1_375.827_611_846_244
943                + 499.154_356_681_091_43 * x
944                + y * (1_349.638_121_077_468 - 325.150_357_510_267 * x
945                    + y * (-819.558_567_859_612 + 298.904_564_555_024 * y)))
946            + z * (-1_158.389_184_128_949_6
947                + x * (1_224.800_896_907_626_3 - 724.212_268_730_786_5 * x)
948                + y * (1_496.252_053_394_976 - 530.695_158_289_722 * x
949                    + y * (-2_139.774_674_491_656
950                        + 461.517_277_301_845_2 * x
951                        + y * (1_362.740_374_087_128 - 437.847_502_801_906 * y)))
952                + z * (380.179_614_640_378 - 122.850_695_678_862 * x
953                    + y * (-759.767_145_265_752
954                        + y * (1_060.896_859_939_2 - 267.078_562_383_391 * y))
955                    + z * (-104.992_062_636_397
956                        + y * (281.863_521_326_18 - 316.800_618_884_673 * y))));
957        Ok(g08 * GSW_SFAC * 0.5e-8 * 1e-8)
958    } else if (ns == 0) & (nt == 1) & (np == 2) {
959        let g03 = 1_552.307_223_226_202
960            + y * (-3_026.233_543_077_436
961                + y * (2_996.162_344_914_912
962                    + y * (-2_414.523_044_975_008
963                        + y * (1_205.206_549_020_25 - 267.476_780_568_550_8 * y))))
964            + z * (-1_179.075_305_287_32
965                + y * (3_281.755_947_882_336
966                    + y * (-4_311.815_951_779_416
967                        + y * (3_652.712_921_612_184
968                            + y * (-1_658.169_157_020_456 + 294.141_795_054_520 * y))))
969                + z * (347.755_831_553_010
970                    + y * (-1_333.449_753_161_323
971                        + y * (1_756.845_066_937_94
972                            + y * (-1_265.992_210_717_45 + 388.914_400_846_332_6 * y)))
973                    + z * (-42.658_016_703_665_4
974                        + y * (173.768_268_766_878_8 - 99.784_263_725_343_0 * y))));
975
976        let g08 = x2
977            * (-687.913_805_923_122_0
978                + 166.384_785_560_363_8 * x
979                + y * (1_349.638_121_077_468 - 216.766_905_006_844_8 * x
980                    + y * (-1_229.337_851_789_418 + 597.809_129_110_048_0 * y))
981                + z * (748.126_026_697_488_0 - 176.898_386_096_574_0 * x
982                    + y * (-2_139.774_674_491_656
983                        + 307.678_184_867_896_8 * x
984                        + y * (2_044.110_561_130_692 - 875.695_005_603_811_3 * y))
985                    + z * (-379.883_572_632_876
986                        + y * (1_060.896_859_939_20 - 400.617_843_575_086_8 * y)
987                        + z * (140.931_760_663_089_8 - 316.800_618_884_672_8 * y))));
988
989        Ok((g03 + g08) * 2.5e-10 * 1e-8)
990        // Note. This derivative of the Gibbs function is in units of (m^3/(K kg)), that is, the pressure of the derivative in Pa.
991    } else {
992        Err(Error::Undefined)
993    }
994}
995
996#[cfg(test)]
997mod test_gibbs {
998    use super::gibbs;
999
1000    #[test]
1001    // NaN input results in NaN output.
1002    // Other libraries using GSW-rs might rely on this behavior to propagate
1003    // and handle invalid elements.
1004    fn nan() {
1005        for ns in 0..=1 {
1006            for nt in 0..=1 {
1007                for np in 0..=1 {
1008                    let v = gibbs(ns, nt, np, f64::NAN, 1.0, 1.0);
1009                    assert!(v.unwrap().is_nan());
1010
1011                    let v = gibbs(ns, nt, np, 1.0, f64::NAN, 1.0);
1012                    assert!(v.unwrap().is_nan());
1013
1014                    let v = gibbs(ns, nt, np, 1.0, 1.0, f64::NAN);
1015                    assert!(v.unwrap().is_nan());
1016                }
1017            }
1018        }
1019    }
1020
1021    #[test]
1022    /// A sanity check with a single set of (S,T,P).
1023    /// Check values match Matlab's output.
1024    fn checking_values() {
1025        let sa = 32.0;
1026        let t = 10.0;
1027        let p = 100.0;
1028        let check_values = [
1029            (0, 0, 0, 50.249_433_022_084_645),
1030            (0, 0, 1, 9.756_573_803_380_614e-4),
1031            (0, 0, 2, -4.319_267_169_958_817e-13),
1032            (0, 1, 0, -144.761_662_219_825_35),
1033            (0, 1, 1, 1.579_419_118_574_033e-7),
1034            (0, 1, 2, 1.731_843_128_190_281e-15),
1035            (0, 2, 0, -14.141_773_253_302_12),
1036            (0, 2, 1, 9.947_007_722_211_213e-9),
1037            (0, 3, 0, 4.830_012_068_790_147e-2),
1038            (1, 0, 0, 60.388_852_305_031_49),
1039            (1, 0, 1, -7.383_641_712_245_458e-7),
1040            (1, 0, 2, 1.306_882_540_566_367e-15),
1041            (1, 1, 0, 0.471_143_208_002_190_2),
1042            (1, 1, 1, 1.849_778_736_567_631e-9),
1043            (1, 2, 0, 1.904_117_940_743_395e-2),
1044            (2, 0, 0, 2.269_679_195_264_845),
1045            (2, 0, 1, 8.949_706_277_002_558e-10),
1046            (2, 1, 0, 1.013_083_889_176_643e-2),
1047        ];
1048
1049        for (ns, nt, np, ans) in check_values.iter() {
1050            assert!((gibbs(*ns, *nt, *np, sa, t, p).unwrap() - *ans).abs() < f64::EPSILON);
1051        }
1052    }
1053}
1054
1055/// Ice specific Gibbs energy and derivatives up to order 2.
1056///
1057/// # Arguments
1058/// - `nt`: `u8` order of t derivative \[integers 0, 1, or 2\]
1059/// - `np`: `u8` order of p derivative \[integers 0, 1, or 2\]
1060/// - `t`: `f64` in-situ temoerature (ITS-90) \[deg C\]
1061/// - `p`: `f64` sea pressure \[dbar\]
1062///
1063/// # Returns
1064/// `gibbs_ice` : `Result<Ok(f64), Err(Error::OutOfBounds)>`
1065///     - specific gibbs energy of ice or its derivatives
1066///     - gibbs energy in \[J/kg\]
1067///     - temperature derivatives in: \[(J/kg) (K)^(-nt)\]
1068///     - pressure derivatives in: \[(J/kg) (Pa)^(-np)\]
1069///     - mixed derivatives in \[(J/kg) (K)^(-nt) (Pa)^(-np)\]
1070#[allow(dead_code)]
1071pub(crate) fn gibbs_ice(nt: u8, np: u8, t: f64, p: f64) -> Result<f64> {
1072    // use a complex number crate. eventually replaced by calculations by hand
1073    use num_complex::Complex;
1074
1075    const GSW_T0: f64 = 273.15;
1076    const REC_TT: f64 = 3.660_858_105_139_845_e-3;
1077    const REC_PT: f64 = 1.634_903_221_903_779_e-3;
1078    const T1: Complex<f64> = Complex::new(3.680_171_128_550_51_e-2, 5.108_781_149_595_72_e-2);
1079    const T2: Complex<f64> = Complex::new(3.373_157_410_654_16_e-1, 3.354_494_159_193_09_e-1);
1080    const G00: f64 = -6.320_202_333_358_86_e5;
1081    const G01: f64 = 6.550_222_136_589_55_e-1;
1082    const G02: f64 = -1.893_699_293_261_31_e-8;
1083    const G03: f64 = 3.397_461_232_710_53_e-15; // in the GSW-C library this is 3.39_746_123_271_053_04e-15
1084    const G04: f64 = -5.564_648_690_589_91_e-22; // in the GSW-C library this is -5.56_464_869_058_990_9e-22
1085    const R21: Complex<f64> = Complex::new(-5.571_076_980_301_23_e-5, 4.645_786_345_808_06_e-5);
1086    const R22: Complex<f64> = Complex::new(2.348_014_092_159_13_e-11, -2.856_511_429_049_72_e-11);
1087    const TT: f64 = 273.16;
1088    // R20 already named
1089    const R20_GIBBS_ICE: Complex<f64> =
1090        Complex::new(-7.259_745_743_292_20_e1, -7.810_084_271_128_70_e1);
1091    // R1 already named
1092    const R1_GIBBS_ICE: Complex<f64> =
1093        Complex::new(4.470_507_162_853_88_e1, 6.568_768_474_634_81_e1);
1094
1095    // declare these initial variables:
1096    let s0: f64 = -3.327_337_564_921_68_e3;
1097    let tau = Complex::new((t + GSW_T0) * REC_TT, 0.0);
1098    let dzi: f64 = DB2PA * p * REC_PT;
1099
1100    // pulling these constants out of the if statements to not be redundant
1101    let tau_t1 = tau / T1;
1102    let sqtau_t1 = tau_t1 * tau_t1;
1103    let tau_t2 = tau / T2;
1104    let sqtau_t2 = tau_t2 * tau_t2;
1105    let r2 = R20_GIBBS_ICE + dzi * (R21 + R22 * dzi);
1106    let r2p = REC_PT * (R21 + 2.0 * R22 * dzi);
1107
1108    if nt == 0 && np == 0 {
1109        let g0: f64 = G00 + dzi * (G01 + dzi * (G02 + dzi * (G03 + dzi * G04)));
1110
1111        let g = R1_GIBBS_ICE
1112            * (tau * ((1.0 + tau_t1) / (1.0 - tau_t1)).ln()
1113                + T1 * ((1.0 - sqtau_t1).ln() - sqtau_t1))
1114            + r2 * ((tau * ((1.0 + tau_t2) / (1.0 - tau_t2)).ln())
1115                + T2 * ((1.0 - sqtau_t2).ln() - sqtau_t2));
1116
1117        Ok((g0 - TT * (s0 * tau - g.re)).re)
1118    } else if nt == 1 && np == 0 {
1119        let g = R1_GIBBS_ICE * (((1.0 + tau_t1) / (1.0 - tau_t1)).ln() - 2.0 * tau_t1)
1120            + r2 * (((1.0 + tau_t2) / (1.0 - tau_t2)).ln() - 2.0 * tau_t2);
1121
1122        Ok(-s0 + g.re)
1123    } else if nt == 0 && np == 1 {
1124        let g0p = REC_PT * (G01 + dzi * (2.0 * G02 + dzi * (3.0 * G03 + 4.0 * G04 * dzi)));
1125
1126        let g = r2p
1127            * (tau * ((1.0 + tau_t2) / (1.0 - tau_t2)).ln()
1128                + T2 * ((1.0 - sqtau_t2).ln() - sqtau_t2));
1129
1130        Ok(g0p + TT * g.re)
1131    } else if nt == 1 && np == 1 {
1132        let g = r2p * (((1.0 + tau_t2) / (1.0 - tau_t2)).ln() - 2.0 * tau_t2);
1133
1134        Ok(g.re)
1135    } else if nt == 2 && np == 0 {
1136        // start to rewrite without num-complex
1137
1138        let g = R1_GIBBS_ICE * (1.0 / (T1 - tau) + 1.0 / (T1 + tau) - 2.0 / T1)
1139            + r2 * (1.0 / (T2 - tau));
1140
1141        Ok(REC_TT * g.re)
1142    } else if nt == 0 && np == 2 {
1143        let sqrec_pt = REC_PT * REC_PT;
1144
1145        let g0pp = sqrec_pt * (2.0 * G02 + dzi * (6.0 * G03 + 12.0 * G04 * dzi));
1146
1147        let r2pp = 2.0 * R22 * sqrec_pt;
1148
1149        let g = r2pp
1150            * (tau * ((1.0 + tau_t2) / (1.0 - tau_t2)).ln()
1151                + T2 * ((1.0 - sqtau_t2).ln() - sqtau_t2));
1152
1153        Ok(g0pp + TT * g.re)
1154    } else {
1155        Err(Error::OutOfBounds)
1156    }
1157}
1158
1159#[cfg(test)]
1160mod test_gibbs_ice {
1161
1162    use crate::gsw_internal_funcs::gibbs_ice;
1163    use crate::Error;
1164
1165    #[test]
1166    fn check_values() {
1167        // answers have not been checked for correctness, but are outputs of the function using num-complex
1168        // (nt, np, ans)
1169        let answers = [
1170            (0, 0, 98.267_598_402_919_25),
1171            (1, 0, 1_220.788_661_299_952_8),
1172            (0, 1, 0.001_090_843_442_926_435),
1173            (1, 1, 1.743_608_249_608_496_2E-7),
1174            (2, 0, -9.078_428_407_946_305),
1175            (0, 2, -1.284_848_246_397_617_7E-13),
1176        ];
1177        for (nt, np, ans) in answers.iter() {
1178            assert!(
1179                (gibbs_ice(*nt, *np, 0.0, 0.0).unwrap() - *ans).abs() < f64::EPSILON,
1180                "nt = {nt}, np = {np}, ans = {ans}"
1181            );
1182        }
1183        //assert!((gibbs_ice(0, 0, 4.0, 10.0).unwrap() - 5.029179813607595e3).abs() < f64::EPSILON); // this is super close
1184    }
1185
1186    #[test]
1187    fn out_of_bounds() {
1188        match gibbs_ice(0, 3, 0.0, 0.0) {
1189            Err(Error::OutOfBounds) => (),
1190            _ => panic!("error should be OutOfBounds"),
1191        }
1192    }
1193
1194    #[test]
1195    // NaN input results in NaN output.
1196    // Other libraries using GSW-rs might rely on this behavior to propagate
1197    // and handle invalid elements.
1198    fn nan() {
1199        let v = gibbs_ice(0, 0, 0.0, f64::NAN);
1200        assert!(v.unwrap().is_nan());
1201
1202        let v = gibbs_ice(0, 0, f64::NAN, 0.0);
1203        assert!(v.unwrap().is_nan());
1204
1205        let v = gibbs_ice(0, 0, f64::NAN, f64::NAN);
1206        assert!(v.unwrap().is_nan());
1207    }
1208}
1209
1210/*
1211gsw_SAAR
1212gsw_Fdelta
1213gsw_deltaSA_atlas
1214gsw_SA_from_SP_Baltic
1215gsw_SP_from_SA_Baltic
1216gsw_infunnel
1217gsw_entropy_part
1218gsw_entropy_part_zerop
1219gsw_quadprog
1220gsw_wiggliness
1221gsw_data_interp
1222gsw_interp_ref_cast
1223gsw_linear_interp_SA_CT
1224gsw_pchip_interp_SA_CT
1225gsw_rr68_interp_SA_CT
1226gsw_spline_interp_SA_CT
1227gsw_gibbs_pt0_pt0
1228gsw_gibbs_ice_part_t
1229gsw_gibbs_ice_pt0
1230*/