From 1c3edbf9009ec4f2d16a4c1dc943093f4a3052c9 Mon Sep 17 00:00:00 2001 From: Eric Firing Date: Mon, 10 Apr 2023 14:48:15 -1000 Subject: [PATCH 1/4] Add gibbs and gibbs_ice via ufunc wrapping. --- gsw/_fixed_wrapped_ufuncs.py | 27 ++++++++ gsw/_utilities.py | 12 +++- gsw/_wrapped_ufuncs.py | 41 +++++++----- src/_ufuncs.c | 118 ++++++++++++++++++++++++++++++++++ tools/c_header_parser.py | 65 ++++++++++++++----- tools/make_ufuncs.py | 120 ++++++++++++++++++++++++++++++++++- tools/make_wrapped_ufuncs.py | 4 ++ 7 files changed, 352 insertions(+), 35 deletions(-) diff --git a/gsw/_fixed_wrapped_ufuncs.py b/gsw/_fixed_wrapped_ufuncs.py index 482584e..0a3698b 100644 --- a/gsw/_fixed_wrapped_ufuncs.py +++ b/gsw/_fixed_wrapped_ufuncs.py @@ -3,6 +3,8 @@ Users should import only from non-private modules, of course. """ +import numpy + from ._wrapped_ufuncs import * _p_from_z = p_from_z @@ -15,5 +17,30 @@ def z_from_p(p, lat, geo_strf_dyn_height=0, sea_surface_geopotential=0): return _z_from_p(p, lat, geo_strf_dyn_height, sea_surface_geopotential) z_from_p.__doc__ = _z_from_p.__doc__ +_gibbs = gibbs +def gibbs(ns, nt, np, sa, t, p): + """ + Docstring to be added... + """ + params = {"ns": ns, "nt": nt, "np": np} + for k, v in params.items(): + u = numpy.unique(v) + if u.min() < 0 or u.max() > 2 or u.dtype.kind != "i": + raise ValueError("ns, nt, np must contain integers 0, 1, or 2;" + f" found {k}={v}") + return _gibbs(ns, nt, np, sa, t, p) + +_gibbs_ice = gibbs_ice +def gibbs_ice(nt, np, t, p): + """ + Docstring to be added... + """ + params = {"nt": nt, "np": np} + for k, v in params.items(): + u = np.unique(v) + if u.min() < 0 or u.max() > 2 or u.dtype.kind != "i": + raise ValueError("nt, np must contain integers 0, 1, or 2;" + f" found {k}={v}") + return _gibbs_ice(nt, np, t, p) diff --git a/gsw/_utilities.py b/gsw/_utilities.py index 0065497..eb59f8a 100644 --- a/gsw/_utilities.py +++ b/gsw/_utilities.py @@ -36,6 +36,14 @@ def wrapper(*args, **kw): hasmasked = np.any(ismasked) hasduck = np.any(isduck) + # Handle the leading integer arguments in gibbs and gibbs_ice. + if hasattr(f, "types"): + argtypes = f.types[0].split("->")[0] + first_double = argtypes.index("d") + else: + first_double = 0 + + def fixup(ret): if hasduck: return ret @@ -50,7 +58,9 @@ def fixup(ret): newargs = [] for i, arg in enumerate(args): - if ismasked[i]: + if i < first_double: + newargs.append(arg) # for gibbs and gibbs_ice + elif ismasked[i]: newargs.append(masked_to_nan(arg)) elif isduck[i]: newargs.append(arg) diff --git a/gsw/_wrapped_ufuncs.py b/gsw/_wrapped_ufuncs.py index 01c22c1..2194550 100644 --- a/gsw/_wrapped_ufuncs.py +++ b/gsw/_wrapped_ufuncs.py @@ -98,7 +98,7 @@ def alpha_on_beta(SA, CT, p): Returns ------- - alpha_on_beta : array-like, kg g^-1 K^-1 + alpha_on_beta : array-like, g kg^-1 K^-1 thermal expansion coefficient with respect to Conservative Temperature divided by the saline contraction coefficient at constant Conservative @@ -310,7 +310,7 @@ def chem_potential_water_t_exact(SA, t, p): @match_args_return def cp_ice(t, p): """ - Calculates the isobaric heat capacity of seawater. + Calculates the isobaric heat capacity of ice. Parameters ---------- @@ -1506,6 +1506,18 @@ def frazil_ratios_adiabatic_poly(SA, p, w_Ih): """ return _gsw_ufuncs.frazil_ratios_adiabatic_poly(SA, p, w_Ih) +@match_args_return +def gibbs(ns, nt, np, SA, t, p): + """(no description available) + """ + return _gsw_ufuncs.gibbs(ns, nt, np, SA, t, p) + +@match_args_return +def gibbs_ice(nt, np, t, p): + """(no description available) + """ + return _gsw_ufuncs.gibbs_ice(nt, np, t, p) + @match_args_return def gibbs_ice_part_t(t, p): """ @@ -2409,7 +2421,7 @@ def pot_enthalpy_ice_freezing_first_derivatives_poly(SA, p): """ Calculates the first derivatives of the potential enthalpy of ice Ih at which ice melts into seawater with Absolute Salinity SA and at pressure - p. This code uses the comptationally efficient polynomial fit of the + p. This code uses the computationally efficient polynomial fit of the freezing potential enthalpy of ice Ih (McDougall et al., 2015). Parameters @@ -2910,12 +2922,11 @@ def rho_first_derivatives(SA, CT, p): @match_args_return def rho_first_derivatives_wrt_enthalpy(SA, CT, p): """ - Calculates the following two first-order derivatives of specific - volume (v), - (1) rho_SA, first-order derivative with respect to Absolute Salinity - at constant CT & p. - (2) rho_h, first-order derivative with respect to SA & CT at - constant p. + Calculates the following two first-order derivatives of rho, + (1) rho_SA_wrt_h, first-order derivative with respect to Absolute + Salinity at constant h & p. + (2) rho_h, first-order derivative with respect to h at + constant SA & p. Parameters ---------- @@ -2928,10 +2939,10 @@ def rho_first_derivatives_wrt_enthalpy(SA, CT, p): Returns ------- - rho_SA : array-like, J/(kg (g/kg)^2) + rho_SA_wrt_h : array-like, ((kg/m^3)(g/kg)^-1 The first derivative of rho with respect to Absolute Salinity at constant CT & p. - rho_h : array-like, J/(kg K(g/kg)) + rho_h : array-like, (m^3/kg)(J/kg)^-1 The first derivative of rho with respect to SA and CT at constant p. @@ -3031,7 +3042,7 @@ def rho_second_derivatives_wrt_enthalpy(SA, CT, p): Returns ------- - rho_SA_SA : array-like, J/(kg (g/kg)^2) + rho_SA_SA : array-like, (kg/m^3)(g/kg)^-2 The second-order derivative of rho with respect to Absolute Salinity at constant h & p. rho_SA_h : array-like, J/(kg K(g/kg)) @@ -3889,7 +3900,7 @@ def specvol_first_derivatives_wrt_enthalpy(SA, CT, p): Returns ------- - v_SA_wrt_h : array-like, (m^3/kg)(g/kg)^-1 (J/kg)^-1 + v_SA_wrt_h : array-like, (m^3/kg)(g/kg)^-1 The first derivative of specific volume with respect to Absolute Salinity at constant CT & p. v_h : array-like, (m^3/kg)(J/kg)^-1 @@ -3957,7 +3968,7 @@ def specvol_second_derivatives(SA, CT, p): v_CT_CT : array-like, (m^3/kg) K^-2) The second derivative of specific volume with respect to CT at constant SA and p. - v_SA_P : array-like, (m^3/kg) Pa^-1 + v_SA_P : array-like, (m^3/kg)(g/kg)^-1 Pa^-1 The second derivative of specific volume with respect to SA and P at constant CT. v_CT_P : array-like, (m^3/kg) K^-1 Pa^-1 @@ -3991,7 +4002,7 @@ def specvol_second_derivatives_wrt_enthalpy(SA, CT, p): Returns ------- - v_SA_SA_wrt_h : array-like, (m^3/kg)(g/kg)^-2 (J/kg)^-1 + v_SA_SA_wrt_h : array-like, (m^3/kg)(g/kg)^-2 The second-order derivative of specific volume with respect to Absolute Salinity at constant h & p. v_SA_h : array-like, (m^3/kg)(g/kg)^-1 (J/kg)^-1 diff --git a/src/_ufuncs.c b/src/_ufuncs.c index b10b282..d34f140 100644 --- a/src/_ufuncs.c +++ b/src/_ufuncs.c @@ -602,6 +602,96 @@ static char types_ddd_ddddd[] = { NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE, }; +/* 2 int in, 2 double in, 1 out */ +static void loop1d_iidd_d(char **args, npy_intp const *dimensions, + npy_intp const* steps, void* data) +{ + npy_intp i; + npy_intp n = dimensions[0]; + char *in0 = args[0]; + npy_intp in_step0 = steps[0]; + char *in1 = args[1]; + npy_intp in_step1 = steps[1]; + char *in2 = args[2]; + npy_intp in_step2 = steps[2]; + char *in3 = args[3]; + npy_intp in_step3 = steps[3]; + char *out0 = args[4]; + npy_intp out_step0 = steps[4]; + double (*func)(int, int, double, double); + double outd0; + func = data; + + for (i = 0; i < n; i++) { + if (isnan(*(double *)in2)||isnan(*(double *)in3)) { + *((double *)out0) = NAN; + } else { + outd0 = func((int)*(long long *)in0, (int)*(long long *)in1, *(double *)in2, *(double *)in3); + *((double *)out0) = CONVERT_INVALID(outd0); + } + in0 += in_step0; + in1 += in_step1; + in2 += in_step2; + in3 += in_step3; + out0 += out_step0; + } +} + +static PyUFuncGenericFunction funcs_iidd_d[] = {&loop1d_iidd_d}; + +static char types_iidd_d[] = { + NPY_INT64, NPY_INT64, + NPY_DOUBLE, NPY_DOUBLE, + NPY_DOUBLE, +}; +/* 3 int in, 3 double in, 1 out */ +static void loop1d_iiiddd_d(char **args, npy_intp const *dimensions, + npy_intp const* steps, void* data) +{ + npy_intp i; + npy_intp n = dimensions[0]; + char *in0 = args[0]; + npy_intp in_step0 = steps[0]; + char *in1 = args[1]; + npy_intp in_step1 = steps[1]; + char *in2 = args[2]; + npy_intp in_step2 = steps[2]; + char *in3 = args[3]; + npy_intp in_step3 = steps[3]; + char *in4 = args[4]; + npy_intp in_step4 = steps[4]; + char *in5 = args[5]; + npy_intp in_step5 = steps[5]; + char *out0 = args[6]; + npy_intp out_step0 = steps[6]; + double (*func)(int, int, int, double, double, double); + double outd0; + func = data; + + for (i = 0; i < n; i++) { + if (isnan(*(double *)in3)||isnan(*(double *)in4)||isnan(*(double *)in5)) { + *((double *)out0) = NAN; + } else { + outd0 = func((int)*(long long *)in0, (int)*(long long *)in1, (int)*(long long *)in2, *(double *)in3, *(double *)in4, *(double *)in5); + *((double *)out0) = CONVERT_INVALID(outd0); + } + in0 += in_step0; + in1 += in_step1; + in2 += in_step2; + in3 += in_step3; + in4 += in_step4; + in5 += in_step5; + out0 += out_step0; + } +} + +static PyUFuncGenericFunction funcs_iiiddd_d[] = {&loop1d_iiiddd_d}; + +static char types_iiiddd_d[] = { + NPY_INT64, NPY_INT64, NPY_INT64, + NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE, + NPY_DOUBLE, +}; static void *data_enthalpy_sso_0[] = {&gsw_enthalpy_sso_0}; static void *data_gibbs_ice_pt0[] = {&gsw_gibbs_ice_pt0}; static void *data_gibbs_ice_pt0_pt0[] = {&gsw_gibbs_ice_pt0_pt0}; @@ -767,6 +857,8 @@ static void *data_melting_ice_into_seawater[] = {&gsw_melting_ice_into_seawater} static void *data_seaice_fraction_to_freeze_seawater[] = {&gsw_seaice_fraction_to_freeze_seawater}; static void *data_rho_second_derivatives[] = {&gsw_rho_second_derivatives}; static void *data_specvol_second_derivatives[] = {&gsw_specvol_second_derivatives}; +static void *data_gibbs_ice[] = {&gsw_gibbs_ice}; +static void *data_gibbs[] = {&gsw_gibbs}; #include "method_bodies.c" @@ -2785,6 +2877,32 @@ PyMODINIT_FUNC PyInit__gsw_ufuncs(void) PyDict_SetItemString(d, "specvol_second_derivatives", ufunc_ptr); Py_DECREF(ufunc_ptr); + ufunc_ptr = PyUFunc_FromFuncAndData(funcs_iidd_d, + data_gibbs_ice, + types_iidd_d, + 1, 4, 1, // ndatatypes, nin, nout + PyUFunc_None, + "gibbs_ice", + "gibbs_ice_docstring", + 0); + + PyDict_SetItemString(d, "gibbs_ice", ufunc_ptr); + Py_DECREF(ufunc_ptr); + + + ufunc_ptr = PyUFunc_FromFuncAndData(funcs_iiiddd_d, + data_gibbs, + types_iiiddd_d, + 1, 6, 1, // ndatatypes, nin, nout + PyUFunc_None, + "gibbs", + "gibbs_docstring", + 0); + + PyDict_SetItemString(d, "gibbs", ufunc_ptr); + Py_DECREF(ufunc_ptr); + + return m; } diff --git a/tools/c_header_parser.py b/tools/c_header_parser.py index 7a84474..8007a40 100644 --- a/tools/c_header_parser.py +++ b/tools/c_header_parser.py @@ -1,10 +1,11 @@ """ Functions for taking apart the function declarations in gswteos-10.h. """ - +from collections import ChainMap +from pathlib import Path import re -from pathlib import Path +import numpy as np basedir = Path('..').resolve() @@ -83,12 +84,16 @@ def parse_signatures(sigs): sigdict[psig['name']] = psig return sigdict +def get_sigdict(srcdir="src"): + return parse_signatures(get_signatures(srcdir=srcdir)) + +# Note: some "sigdict" structures below do *not* use the name as the key. + def simple_sigs(sigdict): """ Given the dict output of parse_signatures, return a dict - with the number of inputs as key, and a list of names as the value. - Only functions returning a double, and with double arguments - are included. + with the *number of inputs as key*, and a list of names as the value. + Only functions with double arguments and return value are included. """ simple = {} for psig in sigdict.values(): @@ -99,18 +104,17 @@ def simple_sigs(sigdict): simple[n].append(psig['name']) else: simple[n] = [psig['name']] - for key, value in simple.items(): + for value in simple.values(): value.sort() - return simple def get_simple_sig_dict(srcdir='src'): - sigs = get_signatures(srcdir=srcdir) - sigdict = parse_signatures(sigs) - simple = simple_sigs(sigdict) - return simple + return simple_sigs(get_sigdict(srcdir="src")) def complex_sigdict(sigdict): + """ + This is a name-keyed sigdict with everything that is *not* in "simple". + """ out = {} for key, psig in sigdict.items(): if (psig['returntype'] == 'double' and @@ -120,11 +124,35 @@ def complex_sigdict(sigdict): return out def get_complex_sigdict(srcdir='src'): - sigs = get_signatures(srcdir=srcdir) - sigdict = parse_signatures(sigs) - return complex_sigdict(sigdict) + return complex_sigdict(get_sigdict(srcdir=srcdir)) + + +def mixed_sigdict(sigdict): + """ + This should find gibbs and gibbs_ice, with their leading int arguments. + It is keyed by name. + """ + out1 = {k: psig for k, psig in sigdict.items() if psig['returntype'] == 'double'} + out = {} + for k, psig in out1.items(): + n_int = np.array([arg == "int" for arg in psig["argtypes"]]).sum() + n_double = np.array([arg == "double" for arg in psig["argtypes"]]).sum() + if n_int > 0 and n_int + n_double == len(psig["argtypes"]): + out[k] = psig + psig["letter_sig"] = f"{''.join([a[0] for a in psig['argtypes']])}_d" + return out + +def get_mixed_sigdict(srcdir="src"): + return mixed_sigdict(get_sigdict(srcdir=srcdir)) def get_complex_scalar_sigdict(srcdir='src'): + """ + Return a name-keyed sigdict for functions with more than one return but + with scalar arguments and return values. + """ + # This works with the current set of functions, but it is not using a fully + # general criterion. It would fail if a scalar function were added with + # more than one output and with integer arguments. cd = get_complex_sigdict(srcdir=srcdir) scalar_dict = {} for k, v in cd.items(): @@ -158,9 +186,12 @@ def print_complex_names_by_nargs_nreturns(srcdir='src'): print(' %s' % name) def print_non_wrappable(srcdir='src'): - csd = get_complex_sigdict(srcdir=srcdir) - scd = get_complex_scalar_sigdict(srcdir=srcdir) - others = [f for f in csd if f not in scd] + sigdict = get_sigdict(srcdir=srcdir) # everything + csd = complex_sigdict(sigdict) # some we wrap, some we don't + scd = get_complex_scalar_sigdict(srcdir=srcdir) # we wrap these + mixed = mixed_sigdict(sigdict) # and these + # Find the names of functions we don't wrap. + others = [k for k in csd if k not in ChainMap(scd, mixed)] othersd = {k : csd[k] for k in others} for k, v in othersd.items(): print(k, v['argstring'], v['returntype']) diff --git a/tools/make_ufuncs.py b/tools/make_ufuncs.py index 70e1350..85e4427 100644 --- a/tools/make_ufuncs.py +++ b/tools/make_ufuncs.py @@ -9,7 +9,8 @@ import shutil from c_header_parser import (get_simple_sig_dict, - get_complex_scalar_dict_by_nargs_nreturns) + get_complex_scalar_dict_by_nargs_nreturns, + get_mixed_sigdict) blacklist = ['add_barrier'] @@ -163,6 +164,90 @@ def modfile_loop_entry(nin, nout): return '\n'.join(linelist) +def modfile_loop_entry_from_sig(sig): + """ + Special case for gibbs, gibbs_ice. + Assume the first half of the args are int, the remainder are double. + Return is a double. + This could all be generalized, but there is probably no need to do so. + It could also be simplified by stripping out the handling of nout > 1. + """ + nin = len(sig["argtypes"]) + nout = 1 + # loop_id = f"{'i' * (nin//2)}{'d' * (nin//2)}_{'d' * nout}" + loop_id = sig["letter_sig"] + linelist = ['/* %d int in, %d double in, %d out */' % (nin//2, nin//2, nout)] + linelist.extend([ + 'static void loop1d_%s(char **args, npy_intp const *dimensions,' % loop_id, + ' npy_intp const* steps, void* data)', + '{', + ' npy_intp i;', + ' npy_intp n = dimensions[0];']) + for i in range(nin): + linelist.append(' char *in%d = args[%d];' % (i, i)) + linelist.append(' npy_intp in_step%d = steps[%d];' % (i, i)) + for i in range(nout): + linelist.append(' char *out%d = args[%d];' % (i, i+nin)) + linelist.append(' npy_intp out_step%d = steps[%d];' % (i, i+nin)) + intypes = ', '.join(['int'] * (nin//2) + ['double'] * (nin//2)) + if nout == 1: + linelist.append(' double (*func)(%s);' % (intypes,)) + else: + outtypes = ', '.join(['double *'] * nout) + linelist.append(' void (*func)(%s, %s);' % (intypes, outtypes)) + + douts = [] + for i in range(nout): + douts.append('outd%d' % (i,)) + linelist.append(' double %s;' % ', '.join(douts)) + linelist.extend([ + ' func = data;', + '', + ' for (i = 0; i < n; i++) {']) + tests = [] + args = [] + for i in range(nin//2, nin): + tests.append('isnan(*(double *)in%d)' % i) + for i in range(nin//2): + args.append('(int)*(long long *)in%d' % i) + for i in range(nin//2, nin): + args.append('*(double *)in%d' % i) + linelist.append(' if (%s) {' % '||'.join(tests)) + outs = [] + for i in range(nout): + outs.append('*((double *)out%d) = NAN;' % i) + linelist.append(' %s' % ''.join(outs)) + linelist.append(' } else {') + if nout > 1: + for i in range(nout): + args.append('&outd%d' % i) + linelist.append(' func(%s);' % ', '.join(args)) + else: + linelist.append(' outd0 = func(%s);' % ', '.join(args)) + for i in range(nout): + linelist.append(' *((double *)out%d)' % (i,) + + ' = CONVERT_INVALID(outd%d);' % (i,)) + linelist.append(' }') + for i in range(nin): + linelist.append(' in%d += in_step%d;' % (i, i)) + for i in range(nout): + linelist.append(' out%d += out_step%d;' % (i, i)) + + linelist.extend([' }', '}', '']) + linelist.append('static PyUFuncGenericFunction' + ' funcs_%s[] = {&loop1d_%s};' % (loop_id, loop_id)) + linelist.append('') + linelist.append('static char types_%s[] = {' % (loop_id,)) + + linelist.append(' ' + 'NPY_INT64, ' * (nin//2)) + linelist.append(' ' + 'NPY_DOUBLE, ' * (nin//2)) + linelist.append(' ' + 'NPY_DOUBLE, ' * nout) + linelist.extend(['};', '']) + + return '\n'.join(linelist) + + + def modfile_array_entry(funcname): return "static void *data_%s[] = {&gsw_%s};\n" % (funcname, funcname) @@ -187,13 +272,36 @@ def modfile_init_entry(funcname, nin, nout): return _init_entry % dict(funcname=funcname, nin=nin, nout=nout, ndin='d'*nin, ndout='d'*nout) +def modfile_init_entry_from_sig(sig): + # Specialized for the gibbs functions. + funcname = sig["name"] + nin = len(sig["argtypes"]) + nout = 1 + letter_sig = sig["letter_sig"] + entry = """ + ufunc_ptr = PyUFunc_FromFuncAndData(funcs_%(letter_sig)s, + data_%(funcname)s, + types_%(letter_sig)s, + 1, %(nin)d, %(nout)d, // ndatatypes, nin, nout + PyUFunc_None, + "%(funcname)s", + "%(funcname)s_docstring", + 0); + + PyDict_SetItemString(d, "%(funcname)s", ufunc_ptr); + Py_DECREF(ufunc_ptr); + + """ + return entry % vars() def write_modfile(modfile_name, srcdir): argcategories1 = get_simple_sig_dict(srcdir=srcdir) argcategories2 = get_complex_scalar_dict_by_nargs_nreturns(srcdir=srcdir) + argcategories3 = get_mixed_sigdict(srcdir=srcdir) funcnamelist1 = [] funcnamelist2 = [] + funcnamelist3 = list(argcategories3.keys()) nins = range(1, 6) artups = [(2, 2), (3, 2), (3, 3), (6, 2), (2, 3), (4, 3), (5, 3), (3, 5)] @@ -207,6 +315,9 @@ def write_modfile(modfile_name, srcdir): chunks = [modfile_head] + for sig in argcategories3.values(): + chunks.append(modfile_loop_entry_from_sig(sig)) + for nin in nins: for funcname in sorted(argcategories1[nin]): if funcname in blacklist: @@ -221,6 +332,9 @@ def write_modfile(modfile_name, srcdir): chunks.append(modfile_array_entry(funcname)) funcnamelist2.append(funcname) + for funcname in funcnamelist3: + chunks.append(modfile_array_entry(funcname)) + chunks.append(modfile_middle) for nin in nins: @@ -235,6 +349,8 @@ def write_modfile(modfile_name, srcdir): continue chunks.append(modfile_init_entry(funcname, *artup)) + for sig in argcategories3.values(): + chunks.append(modfile_init_entry_from_sig(sig)) chunks.append(modfile_tail) @@ -249,7 +365,7 @@ def write_modfile(modfile_name, srcdir): with open(srcdir + '_ufuncs2.list', 'w') as f: f.write('\n'.join(funcnamelist2)) - funcnamelist = funcnamelist1 + funcnamelist2 + funcnamelist = funcnamelist1 + funcnamelist2 + funcnamelist3 funcnamelist.sort() with open(srcdir + '_ufuncs.list', 'w') as f: f.write('\n'.join(funcnamelist)) diff --git a/tools/make_wrapped_ufuncs.py b/tools/make_wrapped_ufuncs.py index 5671b19..f300a60 100644 --- a/tools/make_wrapped_ufuncs.py +++ b/tools/make_wrapped_ufuncs.py @@ -27,6 +27,9 @@ 't_freezing_exact', } +# Functions with integer arguments at the start of the argument list. +first_float_dict = {"gibbs": 3, "gibbs_ice": 2} + wrapper_head = ''' """ Auto-generated wrapper for C ufunc extension; do not edit! @@ -139,6 +142,7 @@ def uf_wrapper(ufname): subs = dict(ufuncname=ufname, funcname=msig['name'], args=argstr, + first_float=first_float_dict.get(msig['name'], 0), ) helpdict = get_helpdict(msig['path']) From 364a586033eb4288113b106fd2aac9ac0b7fc6ee Mon Sep 17 00:00:00 2001 From: Eric Firing Date: Sat, 15 Apr 2023 17:48:25 -1000 Subject: [PATCH 2/4] Docstrings for gibbs and gibbs_ice --- gsw/_fixed_wrapped_ufuncs.py | 13 +- gsw/_utilities.py | 2 + gsw/_wrapped_ufuncs.py | 544 ++++++++++++++++++++++++----------- tools/docstring_parts.py | 27 ++ tools/docstring_utils.py | 1 + tools/make_wrapped_ufuncs.py | 42 ++- tools/matlab_parser.py | 10 + 7 files changed, 462 insertions(+), 177 deletions(-) diff --git a/gsw/_fixed_wrapped_ufuncs.py b/gsw/_fixed_wrapped_ufuncs.py index 0a3698b..8ab15c8 100644 --- a/gsw/_fixed_wrapped_ufuncs.py +++ b/gsw/_fixed_wrapped_ufuncs.py @@ -18,24 +18,19 @@ def z_from_p(p, lat, geo_strf_dyn_height=0, sea_surface_geopotential=0): z_from_p.__doc__ = _z_from_p.__doc__ _gibbs = gibbs -def gibbs(ns, nt, np, sa, t, p): - """ - Docstring to be added... - """ +def gibbs(ns, nt, np, SA, t, p): params = {"ns": ns, "nt": nt, "np": np} for k, v in params.items(): u = numpy.unique(v) if u.min() < 0 or u.max() > 2 or u.dtype.kind != "i": raise ValueError("ns, nt, np must contain integers 0, 1, or 2;" f" found {k}={v}") - return _gibbs(ns, nt, np, sa, t, p) + return _gibbs(ns, nt, np, SA, t, p) +gibbs.__doc__ = _gibbs.__doc__ _gibbs_ice = gibbs_ice def gibbs_ice(nt, np, t, p): - """ - Docstring to be added... - """ params = {"nt": nt, "np": np} for k, v in params.items(): u = np.unique(v) @@ -43,4 +38,4 @@ def gibbs_ice(nt, np, t, p): raise ValueError("nt, np must contain integers 0, 1, or 2;" f" found {k}={v}") return _gibbs_ice(nt, np, t, p) - +gibbs_ice.__doc__ = _gibbs_ice.__doc__ diff --git a/gsw/_utilities.py b/gsw/_utilities.py index eb59f8a..7366850 100644 --- a/gsw/_utilities.py +++ b/gsw/_utilities.py @@ -37,6 +37,8 @@ def wrapper(*args, **kw): hasduck = np.any(isduck) # Handle the leading integer arguments in gibbs and gibbs_ice. + # Wrapped ufuncs are constructed with the "types" attribute from the + # underlying ufunc. if hasattr(f, "types"): argtypes = f.types[0].split("->")[0] first_double = argtypes.index("d") diff --git a/gsw/_wrapped_ufuncs.py b/gsw/_wrapped_ufuncs.py index 2194550..5ccfae1 100644 --- a/gsw/_wrapped_ufuncs.py +++ b/gsw/_wrapped_ufuncs.py @@ -7,7 +7,6 @@ from ._utilities import match_args_return -@match_args_return def adiabatic_lapse_rate_from_CT(SA, CT, p): """ Calculates the adiabatic lapse rate of sea water from Conservative @@ -30,8 +29,9 @@ def adiabatic_lapse_rate_from_CT(SA, CT, p): """ return _gsw_ufuncs.adiabatic_lapse_rate_from_ct(SA, CT, p) +adiabatic_lapse_rate_from_CT.types = _gsw_ufuncs.adiabatic_lapse_rate_from_ct.types +adiabatic_lapse_rate_from_CT = match_args_return(adiabatic_lapse_rate_from_CT) -@match_args_return def adiabatic_lapse_rate_ice(t, p): """ Calculates the adiabatic lapse rate of ice. @@ -51,8 +51,9 @@ def adiabatic_lapse_rate_ice(t, p): """ return _gsw_ufuncs.adiabatic_lapse_rate_ice(t, p) +adiabatic_lapse_rate_ice.types = _gsw_ufuncs.adiabatic_lapse_rate_ice.types +adiabatic_lapse_rate_ice = match_args_return(adiabatic_lapse_rate_ice) -@match_args_return def alpha(SA, CT, p): """ Calculates the thermal expansion coefficient of seawater with respect to @@ -77,8 +78,9 @@ def alpha(SA, CT, p): """ return _gsw_ufuncs.alpha(SA, CT, p) +alpha.types = _gsw_ufuncs.alpha.types +alpha = match_args_return(alpha) -@match_args_return def alpha_on_beta(SA, CT, p): """ Calculates alpha divided by beta, where alpha is the thermal expansion @@ -107,8 +109,9 @@ def alpha_on_beta(SA, CT, p): """ return _gsw_ufuncs.alpha_on_beta(SA, CT, p) +alpha_on_beta.types = _gsw_ufuncs.alpha_on_beta.types +alpha_on_beta = match_args_return(alpha_on_beta) -@match_args_return def alpha_wrt_t_exact(SA, t, p): """ Calculates the thermal expansion coefficient of seawater with respect to @@ -132,8 +135,9 @@ def alpha_wrt_t_exact(SA, t, p): """ return _gsw_ufuncs.alpha_wrt_t_exact(SA, t, p) +alpha_wrt_t_exact.types = _gsw_ufuncs.alpha_wrt_t_exact.types +alpha_wrt_t_exact = match_args_return(alpha_wrt_t_exact) -@match_args_return def alpha_wrt_t_ice(t, p): """ Calculates the thermal expansion coefficient of ice with respect to @@ -155,8 +159,9 @@ def alpha_wrt_t_ice(t, p): """ return _gsw_ufuncs.alpha_wrt_t_ice(t, p) +alpha_wrt_t_ice.types = _gsw_ufuncs.alpha_wrt_t_ice.types +alpha_wrt_t_ice = match_args_return(alpha_wrt_t_ice) -@match_args_return def beta(SA, CT, p): """ Calculates the saline (i.e. haline) contraction coefficient of seawater @@ -182,8 +187,9 @@ def beta(SA, CT, p): """ return _gsw_ufuncs.beta(SA, CT, p) +beta.types = _gsw_ufuncs.beta.types +beta = match_args_return(beta) -@match_args_return def beta_const_t_exact(SA, t, p): """ Calculates the saline (i.e. haline) contraction coefficient of seawater @@ -207,8 +213,9 @@ def beta_const_t_exact(SA, t, p): """ return _gsw_ufuncs.beta_const_t_exact(SA, t, p) +beta_const_t_exact.types = _gsw_ufuncs.beta_const_t_exact.types +beta_const_t_exact = match_args_return(beta_const_t_exact) -@match_args_return def C_from_SP(SP, t, p): """ Calculates conductivity, C, from (SP,t,p) using PSS-78 in the range @@ -234,8 +241,9 @@ def C_from_SP(SP, t, p): """ return _gsw_ufuncs.c_from_sp(SP, t, p) +C_from_SP.types = _gsw_ufuncs.c_from_sp.types +C_from_SP = match_args_return(C_from_SP) -@match_args_return def cabbeling(SA, CT, p): """ Calculates the cabbeling coefficient of seawater with respect to @@ -261,8 +269,9 @@ def cabbeling(SA, CT, p): """ return _gsw_ufuncs.cabbeling(SA, CT, p) +cabbeling.types = _gsw_ufuncs.cabbeling.types +cabbeling = match_args_return(cabbeling) -@match_args_return def chem_potential_water_ice(t, p): """ Calculates the chemical potential of water in ice from in-situ @@ -283,8 +292,9 @@ def chem_potential_water_ice(t, p): """ return _gsw_ufuncs.chem_potential_water_ice(t, p) +chem_potential_water_ice.types = _gsw_ufuncs.chem_potential_water_ice.types +chem_potential_water_ice = match_args_return(chem_potential_water_ice) -@match_args_return def chem_potential_water_t_exact(SA, t, p): """ Calculates the chemical potential of water in seawater. @@ -306,8 +316,9 @@ def chem_potential_water_t_exact(SA, t, p): """ return _gsw_ufuncs.chem_potential_water_t_exact(SA, t, p) +chem_potential_water_t_exact.types = _gsw_ufuncs.chem_potential_water_t_exact.types +chem_potential_water_t_exact = match_args_return(chem_potential_water_t_exact) -@match_args_return def cp_ice(t, p): """ Calculates the isobaric heat capacity of ice. @@ -327,8 +338,9 @@ def cp_ice(t, p): """ return _gsw_ufuncs.cp_ice(t, p) +cp_ice.types = _gsw_ufuncs.cp_ice.types +cp_ice = match_args_return(cp_ice) -@match_args_return def cp_t_exact(SA, t, p): """ Calculates the isobaric heat capacity of seawater. @@ -350,8 +362,9 @@ def cp_t_exact(SA, t, p): """ return _gsw_ufuncs.cp_t_exact(SA, t, p) +cp_t_exact.types = _gsw_ufuncs.cp_t_exact.types +cp_t_exact = match_args_return(cp_t_exact) -@match_args_return def CT_first_derivatives(SA, pt): """ Calculates the following two derivatives of Conservative Temperature @@ -383,8 +396,9 @@ def CT_first_derivatives(SA, pt): """ return _gsw_ufuncs.ct_first_derivatives(SA, pt) +CT_first_derivatives.types = _gsw_ufuncs.ct_first_derivatives.types +CT_first_derivatives = match_args_return(CT_first_derivatives) -@match_args_return def CT_first_derivatives_wrt_t_exact(SA, t, p): """ Calculates the following three derivatives of Conservative Temperature. @@ -423,8 +437,9 @@ def CT_first_derivatives_wrt_t_exact(SA, t, p): """ return _gsw_ufuncs.ct_first_derivatives_wrt_t_exact(SA, t, p) +CT_first_derivatives_wrt_t_exact.types = _gsw_ufuncs.ct_first_derivatives_wrt_t_exact.types +CT_first_derivatives_wrt_t_exact = match_args_return(CT_first_derivatives_wrt_t_exact) -@match_args_return def CT_freezing(SA, p, saturation_fraction): """ Calculates the Conservative Temperature at which seawater freezes. The @@ -452,8 +467,9 @@ def CT_freezing(SA, p, saturation_fraction): """ return _gsw_ufuncs.ct_freezing(SA, p, saturation_fraction) +CT_freezing.types = _gsw_ufuncs.ct_freezing.types +CT_freezing = match_args_return(CT_freezing) -@match_args_return def CT_freezing_first_derivatives(SA, p, saturation_fraction): """ Calculates the first derivatives of the Conservative Temperature at @@ -483,8 +499,9 @@ def CT_freezing_first_derivatives(SA, p, saturation_fraction): """ return _gsw_ufuncs.ct_freezing_first_derivatives(SA, p, saturation_fraction) +CT_freezing_first_derivatives.types = _gsw_ufuncs.ct_freezing_first_derivatives.types +CT_freezing_first_derivatives = match_args_return(CT_freezing_first_derivatives) -@match_args_return def CT_freezing_first_derivatives_poly(SA, p, saturation_fraction): """ Calculates the first derivatives of the Conservative Temperature at @@ -515,8 +532,9 @@ def CT_freezing_first_derivatives_poly(SA, p, saturation_fraction): """ return _gsw_ufuncs.ct_freezing_first_derivatives_poly(SA, p, saturation_fraction) +CT_freezing_first_derivatives_poly.types = _gsw_ufuncs.ct_freezing_first_derivatives_poly.types +CT_freezing_first_derivatives_poly = match_args_return(CT_freezing_first_derivatives_poly) -@match_args_return def CT_freezing_poly(SA, p, saturation_fraction): """ Calculates the Conservative Temperature at which seawater freezes. @@ -546,8 +564,9 @@ def CT_freezing_poly(SA, p, saturation_fraction): """ return _gsw_ufuncs.ct_freezing_poly(SA, p, saturation_fraction) +CT_freezing_poly.types = _gsw_ufuncs.ct_freezing_poly.types +CT_freezing_poly = match_args_return(CT_freezing_poly) -@match_args_return def CT_from_enthalpy(SA, h, p): """ Calculates the Conservative Temperature of seawater, given the Absolute @@ -573,8 +592,9 @@ def CT_from_enthalpy(SA, h, p): """ return _gsw_ufuncs.ct_from_enthalpy(SA, h, p) +CT_from_enthalpy.types = _gsw_ufuncs.ct_from_enthalpy.types +CT_from_enthalpy = match_args_return(CT_from_enthalpy) -@match_args_return def CT_from_enthalpy_exact(SA, h, p): """ Calculates the Conservative Temperature of seawater, given the Absolute @@ -599,8 +619,9 @@ def CT_from_enthalpy_exact(SA, h, p): """ return _gsw_ufuncs.ct_from_enthalpy_exact(SA, h, p) +CT_from_enthalpy_exact.types = _gsw_ufuncs.ct_from_enthalpy_exact.types +CT_from_enthalpy_exact = match_args_return(CT_from_enthalpy_exact) -@match_args_return def CT_from_entropy(SA, entropy): """ Calculates Conservative Temperature with entropy as an input variable. @@ -620,8 +641,9 @@ def CT_from_entropy(SA, entropy): """ return _gsw_ufuncs.ct_from_entropy(SA, entropy) +CT_from_entropy.types = _gsw_ufuncs.ct_from_entropy.types +CT_from_entropy = match_args_return(CT_from_entropy) -@match_args_return def CT_from_pt(SA, pt): """ Calculates Conservative Temperature of seawater from potential @@ -642,8 +664,9 @@ def CT_from_pt(SA, pt): """ return _gsw_ufuncs.ct_from_pt(SA, pt) +CT_from_pt.types = _gsw_ufuncs.ct_from_pt.types +CT_from_pt = match_args_return(CT_from_pt) -@match_args_return def CT_from_rho(rho, SA, p): """ Calculates the Conservative Temperature of a seawater sample, for given @@ -670,8 +693,9 @@ def CT_from_rho(rho, SA, p): """ return _gsw_ufuncs.ct_from_rho(rho, SA, p) +CT_from_rho.types = _gsw_ufuncs.ct_from_rho.types +CT_from_rho = match_args_return(CT_from_rho) -@match_args_return def CT_from_t(SA, t, p): """ Calculates Conservative Temperature of seawater from in-situ @@ -694,8 +718,9 @@ def CT_from_t(SA, t, p): """ return _gsw_ufuncs.ct_from_t(SA, t, p) +CT_from_t.types = _gsw_ufuncs.ct_from_t.types +CT_from_t = match_args_return(CT_from_t) -@match_args_return def CT_maxdensity(SA, p): """ Calculates the Conservative Temperature of maximum density of seawater. @@ -722,8 +747,9 @@ def CT_maxdensity(SA, p): """ return _gsw_ufuncs.ct_maxdensity(SA, p) +CT_maxdensity.types = _gsw_ufuncs.ct_maxdensity.types +CT_maxdensity = match_args_return(CT_maxdensity) -@match_args_return def CT_second_derivatives(SA, pt): """ Calculates the following three, second-order derivatives of Conservative @@ -765,8 +791,9 @@ def CT_second_derivatives(SA, pt): """ return _gsw_ufuncs.ct_second_derivatives(SA, pt) +CT_second_derivatives.types = _gsw_ufuncs.ct_second_derivatives.types +CT_second_derivatives = match_args_return(CT_second_derivatives) -@match_args_return def deltaSA_atlas(p, lon, lat): """ Calculates the Absolute Salinity Anomaly atlas value, SA - SR, in @@ -790,8 +817,9 @@ def deltaSA_atlas(p, lon, lat): """ return _gsw_ufuncs.deltasa_atlas(p, lon, lat) +deltaSA_atlas.types = _gsw_ufuncs.deltasa_atlas.types +deltaSA_atlas = match_args_return(deltaSA_atlas) -@match_args_return def deltaSA_from_SP(SP, p, lon, lat): """ Calculates Absolute Salinity Anomaly from Practical Salinity. Since SP @@ -817,8 +845,9 @@ def deltaSA_from_SP(SP, p, lon, lat): """ return _gsw_ufuncs.deltasa_from_sp(SP, p, lon, lat) +deltaSA_from_SP.types = _gsw_ufuncs.deltasa_from_sp.types +deltaSA_from_SP = match_args_return(deltaSA_from_SP) -@match_args_return def dilution_coefficient_t_exact(SA, t, p): """ Calculates the dilution coefficient of seawater. The dilution @@ -843,8 +872,9 @@ def dilution_coefficient_t_exact(SA, t, p): """ return _gsw_ufuncs.dilution_coefficient_t_exact(SA, t, p) +dilution_coefficient_t_exact.types = _gsw_ufuncs.dilution_coefficient_t_exact.types +dilution_coefficient_t_exact = match_args_return(dilution_coefficient_t_exact) -@match_args_return def dynamic_enthalpy(SA, CT, p): """ Calculates dynamic enthalpy of seawater using the computationally- @@ -869,8 +899,9 @@ def dynamic_enthalpy(SA, CT, p): """ return _gsw_ufuncs.dynamic_enthalpy(SA, CT, p) +dynamic_enthalpy.types = _gsw_ufuncs.dynamic_enthalpy.types +dynamic_enthalpy = match_args_return(dynamic_enthalpy) -@match_args_return def enthalpy(SA, CT, p): """ Calculates specific enthalpy of seawater using the computationally- @@ -894,8 +925,9 @@ def enthalpy(SA, CT, p): """ return _gsw_ufuncs.enthalpy(SA, CT, p) +enthalpy.types = _gsw_ufuncs.enthalpy.types +enthalpy = match_args_return(enthalpy) -@match_args_return def enthalpy_CT_exact(SA, CT, p): """ Calculates specific enthalpy of seawater from Absolute Salinity and @@ -918,8 +950,9 @@ def enthalpy_CT_exact(SA, CT, p): """ return _gsw_ufuncs.enthalpy_ct_exact(SA, CT, p) +enthalpy_CT_exact.types = _gsw_ufuncs.enthalpy_ct_exact.types +enthalpy_CT_exact = match_args_return(enthalpy_CT_exact) -@match_args_return def enthalpy_diff(SA, CT, p_shallow, p_deep): """ Calculates the difference of the specific enthalpy of seawater between @@ -950,8 +983,9 @@ def enthalpy_diff(SA, CT, p_shallow, p_deep): """ return _gsw_ufuncs.enthalpy_diff(SA, CT, p_shallow, p_deep) +enthalpy_diff.types = _gsw_ufuncs.enthalpy_diff.types +enthalpy_diff = match_args_return(enthalpy_diff) -@match_args_return def enthalpy_first_derivatives(SA, CT, p): """ Calculates the following two derivatives of specific enthalpy (h) of @@ -985,8 +1019,9 @@ def enthalpy_first_derivatives(SA, CT, p): """ return _gsw_ufuncs.enthalpy_first_derivatives(SA, CT, p) +enthalpy_first_derivatives.types = _gsw_ufuncs.enthalpy_first_derivatives.types +enthalpy_first_derivatives = match_args_return(enthalpy_first_derivatives) -@match_args_return def enthalpy_first_derivatives_CT_exact(SA, CT, p): """ Calculates the following two derivatives of specific enthalpy, h, @@ -1018,8 +1053,9 @@ def enthalpy_first_derivatives_CT_exact(SA, CT, p): """ return _gsw_ufuncs.enthalpy_first_derivatives_ct_exact(SA, CT, p) +enthalpy_first_derivatives_CT_exact.types = _gsw_ufuncs.enthalpy_first_derivatives_ct_exact.types +enthalpy_first_derivatives_CT_exact = match_args_return(enthalpy_first_derivatives_CT_exact) -@match_args_return def enthalpy_ice(t, p): """ Calculates the specific enthalpy of ice (h_Ih). @@ -1039,8 +1075,9 @@ def enthalpy_ice(t, p): """ return _gsw_ufuncs.enthalpy_ice(t, p) +enthalpy_ice.types = _gsw_ufuncs.enthalpy_ice.types +enthalpy_ice = match_args_return(enthalpy_ice) -@match_args_return def enthalpy_second_derivatives(SA, CT, p): """ Calculates the following three second-order derivatives of specific @@ -1077,8 +1114,9 @@ def enthalpy_second_derivatives(SA, CT, p): """ return _gsw_ufuncs.enthalpy_second_derivatives(SA, CT, p) +enthalpy_second_derivatives.types = _gsw_ufuncs.enthalpy_second_derivatives.types +enthalpy_second_derivatives = match_args_return(enthalpy_second_derivatives) -@match_args_return def enthalpy_second_derivatives_CT_exact(SA, CT, p): """ Calculates the following three second-order derivatives of specific @@ -1114,8 +1152,9 @@ def enthalpy_second_derivatives_CT_exact(SA, CT, p): """ return _gsw_ufuncs.enthalpy_second_derivatives_ct_exact(SA, CT, p) +enthalpy_second_derivatives_CT_exact.types = _gsw_ufuncs.enthalpy_second_derivatives_ct_exact.types +enthalpy_second_derivatives_CT_exact = match_args_return(enthalpy_second_derivatives_CT_exact) -@match_args_return def enthalpy_t_exact(SA, t, p): """ Calculates the specific enthalpy of seawater. @@ -1137,8 +1176,9 @@ def enthalpy_t_exact(SA, t, p): """ return _gsw_ufuncs.enthalpy_t_exact(SA, t, p) +enthalpy_t_exact.types = _gsw_ufuncs.enthalpy_t_exact.types +enthalpy_t_exact = match_args_return(enthalpy_t_exact) -@match_args_return def entropy_first_derivatives(SA, CT): """ Calculates the following two partial derivatives of specific entropy @@ -1168,8 +1208,9 @@ def entropy_first_derivatives(SA, CT): """ return _gsw_ufuncs.entropy_first_derivatives(SA, CT) +entropy_first_derivatives.types = _gsw_ufuncs.entropy_first_derivatives.types +entropy_first_derivatives = match_args_return(entropy_first_derivatives) -@match_args_return def entropy_from_CT(SA, CT): """ Calculates specific entropy of seawater from Conservative Temperature. @@ -1189,8 +1230,9 @@ def entropy_from_CT(SA, CT): """ return _gsw_ufuncs.entropy_from_ct(SA, CT) +entropy_from_CT.types = _gsw_ufuncs.entropy_from_ct.types +entropy_from_CT = match_args_return(entropy_from_CT) -@match_args_return def entropy_from_pt(SA, pt): """ Calculates specific entropy of seawater as a function of potential @@ -1211,8 +1253,9 @@ def entropy_from_pt(SA, pt): """ return _gsw_ufuncs.entropy_from_pt(SA, pt) +entropy_from_pt.types = _gsw_ufuncs.entropy_from_pt.types +entropy_from_pt = match_args_return(entropy_from_pt) -@match_args_return def entropy_from_t(SA, t, p): """ Calculates specific entropy of seawater from in-situ temperature. @@ -1234,8 +1277,9 @@ def entropy_from_t(SA, t, p): """ return _gsw_ufuncs.entropy_from_t(SA, t, p) +entropy_from_t.types = _gsw_ufuncs.entropy_from_t.types +entropy_from_t = match_args_return(entropy_from_t) -@match_args_return def entropy_ice(t, p): """ Calculates specific entropy of ice. @@ -1255,8 +1299,9 @@ def entropy_ice(t, p): """ return _gsw_ufuncs.entropy_ice(t, p) +entropy_ice.types = _gsw_ufuncs.entropy_ice.types +entropy_ice = match_args_return(entropy_ice) -@match_args_return def entropy_second_derivatives(SA, CT): """ Calculates the following three second-order partial derivatives of @@ -1291,8 +1336,9 @@ def entropy_second_derivatives(SA, CT): """ return _gsw_ufuncs.entropy_second_derivatives(SA, CT) +entropy_second_derivatives.types = _gsw_ufuncs.entropy_second_derivatives.types +entropy_second_derivatives = match_args_return(entropy_second_derivatives) -@match_args_return def Fdelta(p, lon, lat): """ Calculates Fdelta from the Absolute Salinity Anomaly Ratio (SAAR). It @@ -1316,8 +1362,9 @@ def Fdelta(p, lon, lat): """ return _gsw_ufuncs.fdelta(p, lon, lat) +Fdelta.types = _gsw_ufuncs.fdelta.types +Fdelta = match_args_return(Fdelta) -@match_args_return def frazil_properties(SA_bulk, h_bulk, p): """ Calculates the mass fraction of ice (mass of ice divided by mass of ice @@ -1357,8 +1404,9 @@ def frazil_properties(SA_bulk, h_bulk, p): """ return _gsw_ufuncs.frazil_properties(SA_bulk, h_bulk, p) +frazil_properties.types = _gsw_ufuncs.frazil_properties.types +frazil_properties = match_args_return(frazil_properties) -@match_args_return def frazil_properties_potential(SA_bulk, h_pot_bulk, p): """ Calculates the mass fraction of ice (mass of ice divided by mass of ice @@ -1398,8 +1446,9 @@ def frazil_properties_potential(SA_bulk, h_pot_bulk, p): """ return _gsw_ufuncs.frazil_properties_potential(SA_bulk, h_pot_bulk, p) +frazil_properties_potential.types = _gsw_ufuncs.frazil_properties_potential.types +frazil_properties_potential = match_args_return(frazil_properties_potential) -@match_args_return def frazil_properties_potential_poly(SA_bulk, h_pot_bulk, p): """ Calculates the mass fraction of ice (mass of ice divided by mass of ice @@ -1439,8 +1488,9 @@ def frazil_properties_potential_poly(SA_bulk, h_pot_bulk, p): """ return _gsw_ufuncs.frazil_properties_potential_poly(SA_bulk, h_pot_bulk, p) +frazil_properties_potential_poly.types = _gsw_ufuncs.frazil_properties_potential_poly.types +frazil_properties_potential_poly = match_args_return(frazil_properties_potential_poly) -@match_args_return def frazil_ratios_adiabatic(SA, p, w_Ih): """ Calculates the ratios of SA, CT and P changes when frazil ice forms or @@ -1472,8 +1522,9 @@ def frazil_ratios_adiabatic(SA, p, w_Ih): """ return _gsw_ufuncs.frazil_ratios_adiabatic(SA, p, w_Ih) +frazil_ratios_adiabatic.types = _gsw_ufuncs.frazil_ratios_adiabatic.types +frazil_ratios_adiabatic = match_args_return(frazil_ratios_adiabatic) -@match_args_return def frazil_ratios_adiabatic_poly(SA, p, w_Ih): """ Calculates the ratios of SA, CT and P changes when frazil ice forms or @@ -1505,20 +1556,83 @@ def frazil_ratios_adiabatic_poly(SA, p, w_Ih): """ return _gsw_ufuncs.frazil_ratios_adiabatic_poly(SA, p, w_Ih) +frazil_ratios_adiabatic_poly.types = _gsw_ufuncs.frazil_ratios_adiabatic_poly.types +frazil_ratios_adiabatic_poly = match_args_return(frazil_ratios_adiabatic_poly) -@match_args_return def gibbs(ns, nt, np, SA, t, p): - """(no description available) + """ + Calculates specific Gibbs energy and its derivatives up to order 3 for + seawater. The Gibbs function for seawater is that of TEOS-10 + (IOC et al., 2010), being the sum of IAPWS-08 for the saline part and + IAPWS-09 for the pure water part. These IAPWS releases are the + officially blessed IAPWS descriptions of Feistel (2008) and the pure + water part of Feistel (2003). Absolute Salinity, SA, in all of the GSW + routines is expressed on the Reference-Composition Salinity Scale of + 2008 (RCSS-08) of Millero et al. (2008). + + Parameters + ---------- + ns : array-like + order of SA derivative, integer in (0, 1, 2) + nt : array-like + order of t derivative, integer in (0, 1, 2) + np : array-like + order of p derivative, integer in (0, 1, 2) + SA : array-like + Absolute Salinity, g/kg + t : array-like + In-situ temperature (ITS-90), degrees C + p : array-like + Sea pressure (absolute pressure minus 10.1325 dbar), dbar + + Returns + ------- + gibbs : array-like + Specific Gibbs energy or its derivatives. + The Gibbs energy (when ns = nt = np = 0) has units of J/kg. + The Absolute Salinity derivatives are output in units of (J/kg) (g/kg)^(-ns). + The temperature derivatives are output in units of (J/kg) (K)^(-nt). + The pressure derivatives are output in units of (J/kg) (Pa)^(-np). + The mixed derivatives are output in units of (J/kg) (g/kg)^(-ns) (K)^(-nt) (Pa)^(-np). + Note: The derivatives are taken with respect to pressure in Pa, not + withstanding that the pressure input into this routine is in dbar. + """ return _gsw_ufuncs.gibbs(ns, nt, np, SA, t, p) +gibbs.types = _gsw_ufuncs.gibbs.types +gibbs = match_args_return(gibbs) -@match_args_return def gibbs_ice(nt, np, t, p): - """(no description available) + """ + Ice specific Gibbs energy and derivatives up to order 2. + + Parameters + ---------- + nt : array-like + order of t derivative, integer in (0, 1, 2) + np : array-like + order of p derivative, integer in (0, 1, 2) + t : array-like + In-situ temperature (ITS-90), degrees C + p : array-like + Sea pressure (absolute pressure minus 10.1325 dbar), dbar + + Returns + ------- + gibbs_ice : array-like + Specific Gibbs energy of ice or its derivatives. + The Gibbs energy (when nt = np = 0) has units of J/kg. + The temperature derivatives are output in units of (J/kg) (K)^(-nt). + The pressure derivatives are output in units of (J/kg) (Pa)^(-np). + The mixed derivatives are output in units of (J/kg) (K)^(-nt) (Pa)^(-np). + Note. The derivatives are taken with respect to pressure in Pa, not + withstanding that the pressure input into this routine is in dbar. + """ return _gsw_ufuncs.gibbs_ice(nt, np, t, p) +gibbs_ice.types = _gsw_ufuncs.gibbs_ice.types +gibbs_ice = match_args_return(gibbs_ice) -@match_args_return def gibbs_ice_part_t(t, p): """ part of the first temperature derivative of Gibbs energy of ice @@ -1539,8 +1653,9 @@ def gibbs_ice_part_t(t, p): """ return _gsw_ufuncs.gibbs_ice_part_t(t, p) +gibbs_ice_part_t.types = _gsw_ufuncs.gibbs_ice_part_t.types +gibbs_ice_part_t = match_args_return(gibbs_ice_part_t) -@match_args_return def gibbs_ice_pt0(pt0): """ part of the first temperature derivative of Gibbs energy of ice @@ -1559,8 +1674,9 @@ def gibbs_ice_pt0(pt0): """ return _gsw_ufuncs.gibbs_ice_pt0(pt0) +gibbs_ice_pt0.types = _gsw_ufuncs.gibbs_ice_pt0.types +gibbs_ice_pt0 = match_args_return(gibbs_ice_pt0) -@match_args_return def gibbs_ice_pt0_pt0(pt0): """ The second temperature derivative of Gibbs energy of ice at the @@ -1580,8 +1696,9 @@ def gibbs_ice_pt0_pt0(pt0): """ return _gsw_ufuncs.gibbs_ice_pt0_pt0(pt0) +gibbs_ice_pt0_pt0.types = _gsw_ufuncs.gibbs_ice_pt0_pt0.types +gibbs_ice_pt0_pt0 = match_args_return(gibbs_ice_pt0_pt0) -@match_args_return def grav(lat, p): """ Calculates acceleration due to gravity as a function of latitude and as @@ -1602,8 +1719,9 @@ def grav(lat, p): """ return _gsw_ufuncs.grav(lat, p) +grav.types = _gsw_ufuncs.grav.types +grav = match_args_return(grav) -@match_args_return def Helmholtz_energy_ice(t, p): """ Calculates the Helmholtz energy of ice. @@ -1623,8 +1741,9 @@ def Helmholtz_energy_ice(t, p): """ return _gsw_ufuncs.helmholtz_energy_ice(t, p) +Helmholtz_energy_ice.types = _gsw_ufuncs.helmholtz_energy_ice.types +Helmholtz_energy_ice = match_args_return(Helmholtz_energy_ice) -@match_args_return def Hill_ratio_at_SP2(t): """ Calculates the Hill ratio, which is the adjustment needed to apply for @@ -1647,8 +1766,9 @@ def Hill_ratio_at_SP2(t): """ return _gsw_ufuncs.hill_ratio_at_sp2(t) +Hill_ratio_at_SP2.types = _gsw_ufuncs.hill_ratio_at_sp2.types +Hill_ratio_at_SP2 = match_args_return(Hill_ratio_at_SP2) -@match_args_return def ice_fraction_to_freeze_seawater(SA, CT, p, t_Ih): """ Calculates the mass fraction of ice (mass of ice divided by mass of ice @@ -1689,8 +1809,9 @@ def ice_fraction_to_freeze_seawater(SA, CT, p, t_Ih): """ return _gsw_ufuncs.ice_fraction_to_freeze_seawater(SA, CT, p, t_Ih) +ice_fraction_to_freeze_seawater.types = _gsw_ufuncs.ice_fraction_to_freeze_seawater.types +ice_fraction_to_freeze_seawater = match_args_return(ice_fraction_to_freeze_seawater) -@match_args_return def internal_energy(SA, CT, p): """ Calculates specific internal energy of seawater using the @@ -1714,8 +1835,9 @@ def internal_energy(SA, CT, p): """ return _gsw_ufuncs.internal_energy(SA, CT, p) +internal_energy.types = _gsw_ufuncs.internal_energy.types +internal_energy = match_args_return(internal_energy) -@match_args_return def internal_energy_ice(t, p): """ Calculates the specific internal energy of ice. @@ -1735,8 +1857,9 @@ def internal_energy_ice(t, p): """ return _gsw_ufuncs.internal_energy_ice(t, p) +internal_energy_ice.types = _gsw_ufuncs.internal_energy_ice.types +internal_energy_ice = match_args_return(internal_energy_ice) -@match_args_return def kappa(SA, CT, p): """ Calculates the isentropic compressibility of seawater. This function @@ -1761,8 +1884,9 @@ def kappa(SA, CT, p): """ return _gsw_ufuncs.kappa(SA, CT, p) +kappa.types = _gsw_ufuncs.kappa.types +kappa = match_args_return(kappa) -@match_args_return def kappa_const_t_ice(t, p): """ Calculates isothermal compressibility of ice. @@ -1784,8 +1908,9 @@ def kappa_const_t_ice(t, p): """ return _gsw_ufuncs.kappa_const_t_ice(t, p) +kappa_const_t_ice.types = _gsw_ufuncs.kappa_const_t_ice.types +kappa_const_t_ice = match_args_return(kappa_const_t_ice) -@match_args_return def kappa_ice(t, p): """ Calculates the isentropic compressibility of ice. @@ -1805,8 +1930,9 @@ def kappa_ice(t, p): """ return _gsw_ufuncs.kappa_ice(t, p) +kappa_ice.types = _gsw_ufuncs.kappa_ice.types +kappa_ice = match_args_return(kappa_ice) -@match_args_return def kappa_t_exact(SA, t, p): """ Calculates the isentropic compressibility of seawater. @@ -1828,8 +1954,9 @@ def kappa_t_exact(SA, t, p): """ return _gsw_ufuncs.kappa_t_exact(SA, t, p) +kappa_t_exact.types = _gsw_ufuncs.kappa_t_exact.types +kappa_t_exact = match_args_return(kappa_t_exact) -@match_args_return def latentheat_evap_CT(SA, CT): """ Calculates latent heat, or enthalpy, of evaporation at p = 0 (the @@ -1853,8 +1980,9 @@ def latentheat_evap_CT(SA, CT): """ return _gsw_ufuncs.latentheat_evap_ct(SA, CT) +latentheat_evap_CT.types = _gsw_ufuncs.latentheat_evap_ct.types +latentheat_evap_CT = match_args_return(latentheat_evap_CT) -@match_args_return def latentheat_evap_t(SA, t): """ Calculates latent heat, or enthalpy, of evaporation at p = 0 (the @@ -1877,8 +2005,9 @@ def latentheat_evap_t(SA, t): """ return _gsw_ufuncs.latentheat_evap_t(SA, t) +latentheat_evap_t.types = _gsw_ufuncs.latentheat_evap_t.types +latentheat_evap_t = match_args_return(latentheat_evap_t) -@match_args_return def latentheat_melting(SA, p): """ Calculates latent heat, or enthalpy, of melting. It is defined in terms @@ -1902,8 +2031,9 @@ def latentheat_melting(SA, p): """ return _gsw_ufuncs.latentheat_melting(SA, p) +latentheat_melting.types = _gsw_ufuncs.latentheat_melting.types +latentheat_melting = match_args_return(latentheat_melting) -@match_args_return def melting_ice_equilibrium_SA_CT_ratio(SA, p): """ Calculates the ratio of SA to CT changes when ice melts into seawater @@ -1934,8 +2064,9 @@ def melting_ice_equilibrium_SA_CT_ratio(SA, p): """ return _gsw_ufuncs.melting_ice_equilibrium_sa_ct_ratio(SA, p) +melting_ice_equilibrium_SA_CT_ratio.types = _gsw_ufuncs.melting_ice_equilibrium_sa_ct_ratio.types +melting_ice_equilibrium_SA_CT_ratio = match_args_return(melting_ice_equilibrium_SA_CT_ratio) -@match_args_return def melting_ice_equilibrium_SA_CT_ratio_poly(SA, p): """ Calculates the ratio of SA to CT changes when ice melts into seawater @@ -1966,8 +2097,9 @@ def melting_ice_equilibrium_SA_CT_ratio_poly(SA, p): """ return _gsw_ufuncs.melting_ice_equilibrium_sa_ct_ratio_poly(SA, p) +melting_ice_equilibrium_SA_CT_ratio_poly.types = _gsw_ufuncs.melting_ice_equilibrium_sa_ct_ratio_poly.types +melting_ice_equilibrium_SA_CT_ratio_poly = match_args_return(melting_ice_equilibrium_SA_CT_ratio_poly) -@match_args_return def melting_ice_into_seawater(SA, CT, p, w_Ih, t_Ih): """ Calculates the final Absolute Salinity, final Conservative Temperature @@ -2008,8 +2140,9 @@ def melting_ice_into_seawater(SA, CT, p, w_Ih, t_Ih): """ return _gsw_ufuncs.melting_ice_into_seawater(SA, CT, p, w_Ih, t_Ih) +melting_ice_into_seawater.types = _gsw_ufuncs.melting_ice_into_seawater.types +melting_ice_into_seawater = match_args_return(melting_ice_into_seawater) -@match_args_return def melting_ice_SA_CT_ratio(SA, CT, p, t_Ih): """ Calculates the ratio of SA to CT changes when ice melts into seawater. @@ -2037,8 +2170,9 @@ def melting_ice_SA_CT_ratio(SA, CT, p, t_Ih): """ return _gsw_ufuncs.melting_ice_sa_ct_ratio(SA, CT, p, t_Ih) +melting_ice_SA_CT_ratio.types = _gsw_ufuncs.melting_ice_sa_ct_ratio.types +melting_ice_SA_CT_ratio = match_args_return(melting_ice_SA_CT_ratio) -@match_args_return def melting_ice_SA_CT_ratio_poly(SA, CT, p, t_Ih): """ Calculates the ratio of SA to CT changes when ice melts into seawater. @@ -2066,8 +2200,9 @@ def melting_ice_SA_CT_ratio_poly(SA, CT, p, t_Ih): """ return _gsw_ufuncs.melting_ice_sa_ct_ratio_poly(SA, CT, p, t_Ih) +melting_ice_SA_CT_ratio_poly.types = _gsw_ufuncs.melting_ice_sa_ct_ratio_poly.types +melting_ice_SA_CT_ratio_poly = match_args_return(melting_ice_SA_CT_ratio_poly) -@match_args_return def melting_seaice_equilibrium_SA_CT_ratio(SA, p): """ Calculates the ratio of SA to CT changes when sea ice melts into @@ -2099,8 +2234,9 @@ def melting_seaice_equilibrium_SA_CT_ratio(SA, p): """ return _gsw_ufuncs.melting_seaice_equilibrium_sa_ct_ratio(SA, p) +melting_seaice_equilibrium_SA_CT_ratio.types = _gsw_ufuncs.melting_seaice_equilibrium_sa_ct_ratio.types +melting_seaice_equilibrium_SA_CT_ratio = match_args_return(melting_seaice_equilibrium_SA_CT_ratio) -@match_args_return def melting_seaice_equilibrium_SA_CT_ratio_poly(SA, p): """ Calculates the ratio of SA to CT changes when sea ice melts into @@ -2132,8 +2268,9 @@ def melting_seaice_equilibrium_SA_CT_ratio_poly(SA, p): """ return _gsw_ufuncs.melting_seaice_equilibrium_sa_ct_ratio_poly(SA, p) +melting_seaice_equilibrium_SA_CT_ratio_poly.types = _gsw_ufuncs.melting_seaice_equilibrium_sa_ct_ratio_poly.types +melting_seaice_equilibrium_SA_CT_ratio_poly = match_args_return(melting_seaice_equilibrium_SA_CT_ratio_poly) -@match_args_return def melting_seaice_into_seawater(SA, CT, p, w_seaice, SA_seaice, t_seaice): """ Calculates the Absolute Salinity and Conservative Temperature that @@ -2169,8 +2306,9 @@ def melting_seaice_into_seawater(SA, CT, p, w_seaice, SA_seaice, t_seaice): """ return _gsw_ufuncs.melting_seaice_into_seawater(SA, CT, p, w_seaice, SA_seaice, t_seaice) +melting_seaice_into_seawater.types = _gsw_ufuncs.melting_seaice_into_seawater.types +melting_seaice_into_seawater = match_args_return(melting_seaice_into_seawater) -@match_args_return def melting_seaice_SA_CT_ratio(SA, CT, p, SA_seaice, t_seaice): """ Calculates the ratio of SA to CT changes when sea ice melts into @@ -2201,8 +2339,9 @@ def melting_seaice_SA_CT_ratio(SA, CT, p, SA_seaice, t_seaice): """ return _gsw_ufuncs.melting_seaice_sa_ct_ratio(SA, CT, p, SA_seaice, t_seaice) +melting_seaice_SA_CT_ratio.types = _gsw_ufuncs.melting_seaice_sa_ct_ratio.types +melting_seaice_SA_CT_ratio = match_args_return(melting_seaice_SA_CT_ratio) -@match_args_return def melting_seaice_SA_CT_ratio_poly(SA, CT, p, SA_seaice, t_seaice): """ Calculates the ratio of SA to CT changes when sea ice melts into @@ -2233,8 +2372,9 @@ def melting_seaice_SA_CT_ratio_poly(SA, CT, p, SA_seaice, t_seaice): """ return _gsw_ufuncs.melting_seaice_sa_ct_ratio_poly(SA, CT, p, SA_seaice, t_seaice) +melting_seaice_SA_CT_ratio_poly.types = _gsw_ufuncs.melting_seaice_sa_ct_ratio_poly.types +melting_seaice_SA_CT_ratio_poly = match_args_return(melting_seaice_SA_CT_ratio_poly) -@match_args_return def O2sol(SA, CT, p, lon, lat): """ Calculates the oxygen concentration expected at equilibrium with air at @@ -2264,8 +2404,9 @@ def O2sol(SA, CT, p, lon, lat): """ return _gsw_ufuncs.o2sol(SA, CT, p, lon, lat) +O2sol.types = _gsw_ufuncs.o2sol.types +O2sol = match_args_return(O2sol) -@match_args_return def O2sol_SP_pt(SP, pt): """ Calculates the oxygen concentration expected at equilibrium with air at @@ -2289,8 +2430,9 @@ def O2sol_SP_pt(SP, pt): """ return _gsw_ufuncs.o2sol_sp_pt(SP, pt) +O2sol_SP_pt.types = _gsw_ufuncs.o2sol_sp_pt.types +O2sol_SP_pt = match_args_return(O2sol_SP_pt) -@match_args_return def p_from_z(z, lat, geo_strf_dyn_height, sea_surface_geopotential): """ Calculates sea pressure from height using computationally-efficient @@ -2322,8 +2464,9 @@ def p_from_z(z, lat, geo_strf_dyn_height, sea_surface_geopotential): """ return _gsw_ufuncs.p_from_z(z, lat, geo_strf_dyn_height, sea_surface_geopotential) +p_from_z.types = _gsw_ufuncs.p_from_z.types +p_from_z = match_args_return(p_from_z) -@match_args_return def pot_enthalpy_from_pt_ice(pt0_ice): """ Calculates the potential enthalpy of ice from potential temperature of @@ -2342,8 +2485,9 @@ def pot_enthalpy_from_pt_ice(pt0_ice): """ return _gsw_ufuncs.pot_enthalpy_from_pt_ice(pt0_ice) +pot_enthalpy_from_pt_ice.types = _gsw_ufuncs.pot_enthalpy_from_pt_ice.types +pot_enthalpy_from_pt_ice = match_args_return(pot_enthalpy_from_pt_ice) -@match_args_return def pot_enthalpy_from_pt_ice_poly(pt0_ice): """ Calculates the potential enthalpy of ice from potential temperature of @@ -2364,8 +2508,9 @@ def pot_enthalpy_from_pt_ice_poly(pt0_ice): """ return _gsw_ufuncs.pot_enthalpy_from_pt_ice_poly(pt0_ice) +pot_enthalpy_from_pt_ice_poly.types = _gsw_ufuncs.pot_enthalpy_from_pt_ice_poly.types +pot_enthalpy_from_pt_ice_poly = match_args_return(pot_enthalpy_from_pt_ice_poly) -@match_args_return def pot_enthalpy_ice_freezing(SA, p): """ Calculates the potential enthalpy of ice at which seawater freezes. @@ -2386,8 +2531,9 @@ def pot_enthalpy_ice_freezing(SA, p): """ return _gsw_ufuncs.pot_enthalpy_ice_freezing(SA, p) +pot_enthalpy_ice_freezing.types = _gsw_ufuncs.pot_enthalpy_ice_freezing.types +pot_enthalpy_ice_freezing = match_args_return(pot_enthalpy_ice_freezing) -@match_args_return def pot_enthalpy_ice_freezing_first_derivatives(SA, p): """ Calculates the first derivatives of the potential enthalpy of ice at @@ -2415,8 +2561,9 @@ def pot_enthalpy_ice_freezing_first_derivatives(SA, p): """ return _gsw_ufuncs.pot_enthalpy_ice_freezing_first_derivatives(SA, p) +pot_enthalpy_ice_freezing_first_derivatives.types = _gsw_ufuncs.pot_enthalpy_ice_freezing_first_derivatives.types +pot_enthalpy_ice_freezing_first_derivatives = match_args_return(pot_enthalpy_ice_freezing_first_derivatives) -@match_args_return def pot_enthalpy_ice_freezing_first_derivatives_poly(SA, p): """ Calculates the first derivatives of the potential enthalpy of ice Ih at @@ -2445,8 +2592,9 @@ def pot_enthalpy_ice_freezing_first_derivatives_poly(SA, p): """ return _gsw_ufuncs.pot_enthalpy_ice_freezing_first_derivatives_poly(SA, p) +pot_enthalpy_ice_freezing_first_derivatives_poly.types = _gsw_ufuncs.pot_enthalpy_ice_freezing_first_derivatives_poly.types +pot_enthalpy_ice_freezing_first_derivatives_poly = match_args_return(pot_enthalpy_ice_freezing_first_derivatives_poly) -@match_args_return def pot_enthalpy_ice_freezing_poly(SA, p): """ Calculates the potential enthalpy of ice at which seawater freezes. @@ -2476,8 +2624,9 @@ def pot_enthalpy_ice_freezing_poly(SA, p): """ return _gsw_ufuncs.pot_enthalpy_ice_freezing_poly(SA, p) +pot_enthalpy_ice_freezing_poly.types = _gsw_ufuncs.pot_enthalpy_ice_freezing_poly.types +pot_enthalpy_ice_freezing_poly = match_args_return(pot_enthalpy_ice_freezing_poly) -@match_args_return def pot_rho_t_exact(SA, t, p, p_ref): """ Calculates potential density of seawater. Note. This function outputs @@ -2503,8 +2652,9 @@ def pot_rho_t_exact(SA, t, p, p_ref): """ return _gsw_ufuncs.pot_rho_t_exact(SA, t, p, p_ref) +pot_rho_t_exact.types = _gsw_ufuncs.pot_rho_t_exact.types +pot_rho_t_exact = match_args_return(pot_rho_t_exact) -@match_args_return def pressure_coefficient_ice(t, p): """ Calculates pressure coefficient of ice. @@ -2524,8 +2674,9 @@ def pressure_coefficient_ice(t, p): """ return _gsw_ufuncs.pressure_coefficient_ice(t, p) +pressure_coefficient_ice.types = _gsw_ufuncs.pressure_coefficient_ice.types +pressure_coefficient_ice = match_args_return(pressure_coefficient_ice) -@match_args_return def pressure_freezing_CT(SA, CT, saturation_fraction): """ Calculates the pressure (in dbar) of seawater at the freezing @@ -2554,8 +2705,9 @@ def pressure_freezing_CT(SA, CT, saturation_fraction): """ return _gsw_ufuncs.pressure_freezing_ct(SA, CT, saturation_fraction) +pressure_freezing_CT.types = _gsw_ufuncs.pressure_freezing_ct.types +pressure_freezing_CT = match_args_return(pressure_freezing_CT) -@match_args_return def pt0_from_t(SA, t, p): """ Calculates potential temperature with reference pressure, p_ref = 0 dbar. @@ -2583,8 +2735,9 @@ def pt0_from_t(SA, t, p): """ return _gsw_ufuncs.pt0_from_t(SA, t, p) +pt0_from_t.types = _gsw_ufuncs.pt0_from_t.types +pt0_from_t = match_args_return(pt0_from_t) -@match_args_return def pt0_from_t_ice(t, p): """ Calculates potential temperature of ice Ih with a reference pressure of @@ -2606,8 +2759,9 @@ def pt0_from_t_ice(t, p): """ return _gsw_ufuncs.pt0_from_t_ice(t, p) +pt0_from_t_ice.types = _gsw_ufuncs.pt0_from_t_ice.types +pt0_from_t_ice = match_args_return(pt0_from_t_ice) -@match_args_return def pt_first_derivatives(SA, CT): """ Calculates the following two partial derivatives of potential @@ -2638,8 +2792,9 @@ def pt_first_derivatives(SA, CT): """ return _gsw_ufuncs.pt_first_derivatives(SA, CT) +pt_first_derivatives.types = _gsw_ufuncs.pt_first_derivatives.types +pt_first_derivatives = match_args_return(pt_first_derivatives) -@match_args_return def pt_from_CT(SA, CT): """ Calculates potential temperature (with a reference sea pressure of @@ -2664,8 +2819,9 @@ def pt_from_CT(SA, CT): """ return _gsw_ufuncs.pt_from_ct(SA, CT) +pt_from_CT.types = _gsw_ufuncs.pt_from_ct.types +pt_from_CT = match_args_return(pt_from_CT) -@match_args_return def pt_from_entropy(SA, entropy): """ Calculates potential temperature with reference pressure p_ref = 0 dbar @@ -2687,8 +2843,9 @@ def pt_from_entropy(SA, entropy): """ return _gsw_ufuncs.pt_from_entropy(SA, entropy) +pt_from_entropy.types = _gsw_ufuncs.pt_from_entropy.types +pt_from_entropy = match_args_return(pt_from_entropy) -@match_args_return def pt_from_pot_enthalpy_ice(pot_enthalpy_ice): """ Calculates the potential temperature of ice from the potential enthalpy @@ -2708,8 +2865,9 @@ def pt_from_pot_enthalpy_ice(pot_enthalpy_ice): """ return _gsw_ufuncs.pt_from_pot_enthalpy_ice(pot_enthalpy_ice) +pt_from_pot_enthalpy_ice.types = _gsw_ufuncs.pt_from_pot_enthalpy_ice.types +pt_from_pot_enthalpy_ice = match_args_return(pt_from_pot_enthalpy_ice) -@match_args_return def pt_from_pot_enthalpy_ice_poly(pot_enthalpy_ice): """ Calculates the potential temperature of ice (whose reference sea @@ -2730,8 +2888,9 @@ def pt_from_pot_enthalpy_ice_poly(pot_enthalpy_ice): """ return _gsw_ufuncs.pt_from_pot_enthalpy_ice_poly(pot_enthalpy_ice) +pt_from_pot_enthalpy_ice_poly.types = _gsw_ufuncs.pt_from_pot_enthalpy_ice_poly.types +pt_from_pot_enthalpy_ice_poly = match_args_return(pt_from_pot_enthalpy_ice_poly) -@match_args_return def pt_from_t(SA, t, p, p_ref): """ Calculates potential temperature with the general reference pressure, @@ -2761,8 +2920,9 @@ def pt_from_t(SA, t, p, p_ref): """ return _gsw_ufuncs.pt_from_t(SA, t, p, p_ref) +pt_from_t.types = _gsw_ufuncs.pt_from_t.types +pt_from_t = match_args_return(pt_from_t) -@match_args_return def pt_from_t_ice(t, p, p_ref): """ Calculates potential temperature of ice Ih with the general reference @@ -2786,8 +2946,9 @@ def pt_from_t_ice(t, p, p_ref): """ return _gsw_ufuncs.pt_from_t_ice(t, p, p_ref) +pt_from_t_ice.types = _gsw_ufuncs.pt_from_t_ice.types +pt_from_t_ice = match_args_return(pt_from_t_ice) -@match_args_return def pt_second_derivatives(SA, CT): """ Calculates the following three second-order derivatives of potential @@ -2825,8 +2986,9 @@ def pt_second_derivatives(SA, CT): """ return _gsw_ufuncs.pt_second_derivatives(SA, CT) +pt_second_derivatives.types = _gsw_ufuncs.pt_second_derivatives.types +pt_second_derivatives = match_args_return(pt_second_derivatives) -@match_args_return def rho(SA, CT, p): """ Calculates in-situ density from Absolute Salinity and Conservative @@ -2850,8 +3012,9 @@ def rho(SA, CT, p): """ return _gsw_ufuncs.rho(SA, CT, p) +rho.types = _gsw_ufuncs.rho.types +rho = match_args_return(rho) -@match_args_return def rho_alpha_beta(SA, CT, p): """ Calculates in-situ density, the appropriate thermal expansion coefficient @@ -2883,8 +3046,9 @@ def rho_alpha_beta(SA, CT, p): """ return _gsw_ufuncs.rho_alpha_beta(SA, CT, p) +rho_alpha_beta.types = _gsw_ufuncs.rho_alpha_beta.types +rho_alpha_beta = match_args_return(rho_alpha_beta) -@match_args_return def rho_first_derivatives(SA, CT, p): """ Calculates the three (3) partial derivatives of in-situ density with @@ -2918,8 +3082,9 @@ def rho_first_derivatives(SA, CT, p): """ return _gsw_ufuncs.rho_first_derivatives(SA, CT, p) +rho_first_derivatives.types = _gsw_ufuncs.rho_first_derivatives.types +rho_first_derivatives = match_args_return(rho_first_derivatives) -@match_args_return def rho_first_derivatives_wrt_enthalpy(SA, CT, p): """ Calculates the following two first-order derivatives of rho, @@ -2949,8 +3114,9 @@ def rho_first_derivatives_wrt_enthalpy(SA, CT, p): """ return _gsw_ufuncs.rho_first_derivatives_wrt_enthalpy(SA, CT, p) +rho_first_derivatives_wrt_enthalpy.types = _gsw_ufuncs.rho_first_derivatives_wrt_enthalpy.types +rho_first_derivatives_wrt_enthalpy = match_args_return(rho_first_derivatives_wrt_enthalpy) -@match_args_return def rho_ice(t, p): """ Calculates in-situ density of ice from in-situ temperature and pressure. @@ -2972,8 +3138,9 @@ def rho_ice(t, p): """ return _gsw_ufuncs.rho_ice(t, p) +rho_ice.types = _gsw_ufuncs.rho_ice.types +rho_ice = match_args_return(rho_ice) -@match_args_return def rho_second_derivatives(SA, CT, p): """ Calculates the following five second-order derivatives of rho, @@ -3018,8 +3185,9 @@ def rho_second_derivatives(SA, CT, p): """ return _gsw_ufuncs.rho_second_derivatives(SA, CT, p) +rho_second_derivatives.types = _gsw_ufuncs.rho_second_derivatives.types +rho_second_derivatives = match_args_return(rho_second_derivatives) -@match_args_return def rho_second_derivatives_wrt_enthalpy(SA, CT, p): """ Calculates the following three second-order derivatives of rho with @@ -3055,8 +3223,9 @@ def rho_second_derivatives_wrt_enthalpy(SA, CT, p): """ return _gsw_ufuncs.rho_second_derivatives_wrt_enthalpy(SA, CT, p) +rho_second_derivatives_wrt_enthalpy.types = _gsw_ufuncs.rho_second_derivatives_wrt_enthalpy.types +rho_second_derivatives_wrt_enthalpy = match_args_return(rho_second_derivatives_wrt_enthalpy) -@match_args_return def rho_t_exact(SA, t, p): """ Calculates in-situ density of seawater from Absolute Salinity and @@ -3080,8 +3249,9 @@ def rho_t_exact(SA, t, p): """ return _gsw_ufuncs.rho_t_exact(SA, t, p) +rho_t_exact.types = _gsw_ufuncs.rho_t_exact.types +rho_t_exact = match_args_return(rho_t_exact) -@match_args_return def SA_freezing_from_CT(CT, p, saturation_fraction): """ Calculates the Absolute Salinity of seawater at the freezing temperature. @@ -3111,8 +3281,9 @@ def SA_freezing_from_CT(CT, p, saturation_fraction): """ return _gsw_ufuncs.sa_freezing_from_ct(CT, p, saturation_fraction) +SA_freezing_from_CT.types = _gsw_ufuncs.sa_freezing_from_ct.types +SA_freezing_from_CT = match_args_return(SA_freezing_from_CT) -@match_args_return def SA_freezing_from_CT_poly(CT, p, saturation_fraction): """ Calculates the Absolute Salinity of seawater at the freezing temperature. @@ -3141,8 +3312,9 @@ def SA_freezing_from_CT_poly(CT, p, saturation_fraction): """ return _gsw_ufuncs.sa_freezing_from_ct_poly(CT, p, saturation_fraction) +SA_freezing_from_CT_poly.types = _gsw_ufuncs.sa_freezing_from_ct_poly.types +SA_freezing_from_CT_poly = match_args_return(SA_freezing_from_CT_poly) -@match_args_return def SA_freezing_from_t(t, p, saturation_fraction): """ Calculates the Absolute Salinity of seawater at the freezing temperature. @@ -3171,8 +3343,9 @@ def SA_freezing_from_t(t, p, saturation_fraction): """ return _gsw_ufuncs.sa_freezing_from_t(t, p, saturation_fraction) +SA_freezing_from_t.types = _gsw_ufuncs.sa_freezing_from_t.types +SA_freezing_from_t = match_args_return(SA_freezing_from_t) -@match_args_return def SA_freezing_from_t_poly(t, p, saturation_fraction): """ Calculates the Absolute Salinity of seawater at the freezing temperature. @@ -3201,8 +3374,9 @@ def SA_freezing_from_t_poly(t, p, saturation_fraction): """ return _gsw_ufuncs.sa_freezing_from_t_poly(t, p, saturation_fraction) +SA_freezing_from_t_poly.types = _gsw_ufuncs.sa_freezing_from_t_poly.types +SA_freezing_from_t_poly = match_args_return(SA_freezing_from_t_poly) -@match_args_return def SA_from_rho(rho, CT, p): """ Calculates the Absolute Salinity of a seawater sample, for given values @@ -3227,8 +3401,9 @@ def SA_from_rho(rho, CT, p): """ return _gsw_ufuncs.sa_from_rho(rho, CT, p) +SA_from_rho.types = _gsw_ufuncs.sa_from_rho.types +SA_from_rho = match_args_return(SA_from_rho) -@match_args_return def SA_from_SP(SP, p, lon, lat): """ Calculates Absolute Salinity from Practical Salinity. Since SP is @@ -3254,8 +3429,9 @@ def SA_from_SP(SP, p, lon, lat): """ return _gsw_ufuncs.sa_from_sp(SP, p, lon, lat) +SA_from_SP.types = _gsw_ufuncs.sa_from_sp.types +SA_from_SP = match_args_return(SA_from_SP) -@match_args_return def SA_from_SP_Baltic(SP, lon, lat): """ Calculates Absolute Salinity in the Baltic Sea, from Practical Salinity. @@ -3281,8 +3457,9 @@ def SA_from_SP_Baltic(SP, lon, lat): """ return _gsw_ufuncs.sa_from_sp_baltic(SP, lon, lat) +SA_from_SP_Baltic.types = _gsw_ufuncs.sa_from_sp_baltic.types +SA_from_SP_Baltic = match_args_return(SA_from_SP_Baltic) -@match_args_return def SA_from_Sstar(Sstar, p, lon, lat): """ Calculates Absolute Salinity from Preformed Salinity. @@ -3306,8 +3483,9 @@ def SA_from_Sstar(Sstar, p, lon, lat): """ return _gsw_ufuncs.sa_from_sstar(Sstar, p, lon, lat) +SA_from_Sstar.types = _gsw_ufuncs.sa_from_sstar.types +SA_from_Sstar = match_args_return(SA_from_Sstar) -@match_args_return def SAAR(p, lon, lat): """ Calculates the Absolute Salinity Anomaly Ratio, SAAR, in the open ocean @@ -3331,8 +3509,9 @@ def SAAR(p, lon, lat): """ return _gsw_ufuncs.saar(p, lon, lat) +SAAR.types = _gsw_ufuncs.saar.types +SAAR = match_args_return(SAAR) -@match_args_return def seaice_fraction_to_freeze_seawater(SA, CT, p, SA_seaice, t_seaice): """ Calculates the mass fraction of sea ice (mass of sea ice divided by mass @@ -3376,8 +3555,9 @@ def seaice_fraction_to_freeze_seawater(SA, CT, p, SA_seaice, t_seaice): """ return _gsw_ufuncs.seaice_fraction_to_freeze_seawater(SA, CT, p, SA_seaice, t_seaice) +seaice_fraction_to_freeze_seawater.types = _gsw_ufuncs.seaice_fraction_to_freeze_seawater.types +seaice_fraction_to_freeze_seawater = match_args_return(seaice_fraction_to_freeze_seawater) -@match_args_return def sigma0(SA, CT): """ Calculates potential density anomaly with reference pressure of 0 dbar, @@ -3403,8 +3583,9 @@ def sigma0(SA, CT): """ return _gsw_ufuncs.sigma0(SA, CT) +sigma0.types = _gsw_ufuncs.sigma0.types +sigma0 = match_args_return(sigma0) -@match_args_return def sigma1(SA, CT): """ Calculates potential density anomaly with reference pressure of 1000 @@ -3431,8 +3612,9 @@ def sigma1(SA, CT): """ return _gsw_ufuncs.sigma1(SA, CT) +sigma1.types = _gsw_ufuncs.sigma1.types +sigma1 = match_args_return(sigma1) -@match_args_return def sigma2(SA, CT): """ Calculates potential density anomaly with reference pressure of 2000 @@ -3458,8 +3640,9 @@ def sigma2(SA, CT): """ return _gsw_ufuncs.sigma2(SA, CT) +sigma2.types = _gsw_ufuncs.sigma2.types +sigma2 = match_args_return(sigma2) -@match_args_return def sigma3(SA, CT): """ Calculates potential density anomaly with reference pressure of 3000 @@ -3485,8 +3668,9 @@ def sigma3(SA, CT): """ return _gsw_ufuncs.sigma3(SA, CT) +sigma3.types = _gsw_ufuncs.sigma3.types +sigma3 = match_args_return(sigma3) -@match_args_return def sigma4(SA, CT): """ Calculates potential density anomaly with reference pressure of 4000 @@ -3512,8 +3696,9 @@ def sigma4(SA, CT): """ return _gsw_ufuncs.sigma4(SA, CT) +sigma4.types = _gsw_ufuncs.sigma4.types +sigma4 = match_args_return(sigma4) -@match_args_return def sound_speed(SA, CT, p): """ Calculates the speed of sound in seawater. This function has inputs of @@ -3538,8 +3723,9 @@ def sound_speed(SA, CT, p): """ return _gsw_ufuncs.sound_speed(SA, CT, p) +sound_speed.types = _gsw_ufuncs.sound_speed.types +sound_speed = match_args_return(sound_speed) -@match_args_return def sound_speed_ice(t, p): """ Calculates the compression speed of sound in ice. @@ -3559,8 +3745,9 @@ def sound_speed_ice(t, p): """ return _gsw_ufuncs.sound_speed_ice(t, p) +sound_speed_ice.types = _gsw_ufuncs.sound_speed_ice.types +sound_speed_ice = match_args_return(sound_speed_ice) -@match_args_return def sound_speed_t_exact(SA, t, p): """ Calculates the speed of sound in seawater. @@ -3582,8 +3769,9 @@ def sound_speed_t_exact(SA, t, p): """ return _gsw_ufuncs.sound_speed_t_exact(SA, t, p) +sound_speed_t_exact.types = _gsw_ufuncs.sound_speed_t_exact.types +sound_speed_t_exact = match_args_return(sound_speed_t_exact) -@match_args_return def SP_from_C(C, t, p): """ Calculates Practical Salinity, SP, from conductivity, C, primarily using @@ -3613,8 +3801,9 @@ def SP_from_C(C, t, p): """ return _gsw_ufuncs.sp_from_c(C, t, p) +SP_from_C.types = _gsw_ufuncs.sp_from_c.types +SP_from_C = match_args_return(SP_from_C) -@match_args_return def SP_from_SA(SA, p, lon, lat): """ Calculates Practical Salinity from Absolute Salinity. @@ -3638,8 +3827,9 @@ def SP_from_SA(SA, p, lon, lat): """ return _gsw_ufuncs.sp_from_sa(SA, p, lon, lat) +SP_from_SA.types = _gsw_ufuncs.sp_from_sa.types +SP_from_SA = match_args_return(SP_from_SA) -@match_args_return def SP_from_SA_Baltic(SA, lon, lat): """ Calculates Practical Salinity for the Baltic Sea, from a value computed @@ -3664,8 +3854,9 @@ def SP_from_SA_Baltic(SA, lon, lat): """ return _gsw_ufuncs.sp_from_sa_baltic(SA, lon, lat) +SP_from_SA_Baltic.types = _gsw_ufuncs.sp_from_sa_baltic.types +SP_from_SA_Baltic = match_args_return(SP_from_SA_Baltic) -@match_args_return def SP_from_SK(SK): """ Calculates Practical Salinity from Knudsen Salinity. @@ -3683,8 +3874,9 @@ def SP_from_SK(SK): """ return _gsw_ufuncs.sp_from_sk(SK) +SP_from_SK.types = _gsw_ufuncs.sp_from_sk.types +SP_from_SK = match_args_return(SP_from_SK) -@match_args_return def SP_from_SR(SR): """ Calculates Practical Salinity from Reference Salinity. @@ -3702,8 +3894,9 @@ def SP_from_SR(SR): """ return _gsw_ufuncs.sp_from_sr(SR) +SP_from_SR.types = _gsw_ufuncs.sp_from_sr.types +SP_from_SR = match_args_return(SP_from_SR) -@match_args_return def SP_from_Sstar(Sstar, p, lon, lat): """ Calculates Practical Salinity from Preformed Salinity. @@ -3727,8 +3920,9 @@ def SP_from_Sstar(Sstar, p, lon, lat): """ return _gsw_ufuncs.sp_from_sstar(Sstar, p, lon, lat) +SP_from_Sstar.types = _gsw_ufuncs.sp_from_sstar.types +SP_from_Sstar = match_args_return(SP_from_Sstar) -@match_args_return def SP_salinometer(Rt, t): """ Calculates Practical Salinity SP from a salinometer, primarily using the @@ -3755,8 +3949,9 @@ def SP_salinometer(Rt, t): """ return _gsw_ufuncs.sp_salinometer(Rt, t) +SP_salinometer.types = _gsw_ufuncs.sp_salinometer.types +SP_salinometer = match_args_return(SP_salinometer) -@match_args_return def specvol(SA, CT, p): """ Calculates specific volume from Absolute Salinity, Conservative @@ -3780,8 +3975,9 @@ def specvol(SA, CT, p): """ return _gsw_ufuncs.specvol(SA, CT, p) +specvol.types = _gsw_ufuncs.specvol.types +specvol = match_args_return(specvol) -@match_args_return def specvol_alpha_beta(SA, CT, p): """ Calculates specific volume, the appropriate thermal expansion coefficient @@ -3813,8 +4009,9 @@ def specvol_alpha_beta(SA, CT, p): """ return _gsw_ufuncs.specvol_alpha_beta(SA, CT, p) +specvol_alpha_beta.types = _gsw_ufuncs.specvol_alpha_beta.types +specvol_alpha_beta = match_args_return(specvol_alpha_beta) -@match_args_return def specvol_anom_standard(SA, CT, p): """ Calculates specific volume anomaly from Absolute Salinity, Conservative @@ -3841,8 +4038,9 @@ def specvol_anom_standard(SA, CT, p): """ return _gsw_ufuncs.specvol_anom_standard(SA, CT, p) +specvol_anom_standard.types = _gsw_ufuncs.specvol_anom_standard.types +specvol_anom_standard = match_args_return(specvol_anom_standard) -@match_args_return def specvol_first_derivatives(SA, CT, p): """ Calculates the following three first-order derivatives of specific @@ -3878,8 +4076,9 @@ def specvol_first_derivatives(SA, CT, p): """ return _gsw_ufuncs.specvol_first_derivatives(SA, CT, p) +specvol_first_derivatives.types = _gsw_ufuncs.specvol_first_derivatives.types +specvol_first_derivatives = match_args_return(specvol_first_derivatives) -@match_args_return def specvol_first_derivatives_wrt_enthalpy(SA, CT, p): """ Calculates the following two first-order derivatives of specific @@ -3910,8 +4109,9 @@ def specvol_first_derivatives_wrt_enthalpy(SA, CT, p): """ return _gsw_ufuncs.specvol_first_derivatives_wrt_enthalpy(SA, CT, p) +specvol_first_derivatives_wrt_enthalpy.types = _gsw_ufuncs.specvol_first_derivatives_wrt_enthalpy.types +specvol_first_derivatives_wrt_enthalpy = match_args_return(specvol_first_derivatives_wrt_enthalpy) -@match_args_return def specvol_ice(t, p): """ Calculates the specific volume of ice. @@ -3931,8 +4131,9 @@ def specvol_ice(t, p): """ return _gsw_ufuncs.specvol_ice(t, p) +specvol_ice.types = _gsw_ufuncs.specvol_ice.types +specvol_ice = match_args_return(specvol_ice) -@match_args_return def specvol_second_derivatives(SA, CT, p): """ Calculates the following five second-order derivatives of specific @@ -3978,8 +4179,9 @@ def specvol_second_derivatives(SA, CT, p): """ return _gsw_ufuncs.specvol_second_derivatives(SA, CT, p) +specvol_second_derivatives.types = _gsw_ufuncs.specvol_second_derivatives.types +specvol_second_derivatives = match_args_return(specvol_second_derivatives) -@match_args_return def specvol_second_derivatives_wrt_enthalpy(SA, CT, p): """ Calculates the following three first-order derivatives of specific @@ -4015,8 +4217,9 @@ def specvol_second_derivatives_wrt_enthalpy(SA, CT, p): """ return _gsw_ufuncs.specvol_second_derivatives_wrt_enthalpy(SA, CT, p) +specvol_second_derivatives_wrt_enthalpy.types = _gsw_ufuncs.specvol_second_derivatives_wrt_enthalpy.types +specvol_second_derivatives_wrt_enthalpy = match_args_return(specvol_second_derivatives_wrt_enthalpy) -@match_args_return def specvol_t_exact(SA, t, p): """ Calculates the specific volume of seawater. @@ -4038,8 +4241,9 @@ def specvol_t_exact(SA, t, p): """ return _gsw_ufuncs.specvol_t_exact(SA, t, p) +specvol_t_exact.types = _gsw_ufuncs.specvol_t_exact.types +specvol_t_exact = match_args_return(specvol_t_exact) -@match_args_return def spiciness0(SA, CT): """ Calculates spiciness from Absolute Salinity and Conservative @@ -4064,8 +4268,9 @@ def spiciness0(SA, CT): """ return _gsw_ufuncs.spiciness0(SA, CT) +spiciness0.types = _gsw_ufuncs.spiciness0.types +spiciness0 = match_args_return(spiciness0) -@match_args_return def spiciness1(SA, CT): """ Calculates spiciness from Absolute Salinity and Conservative @@ -4089,8 +4294,9 @@ def spiciness1(SA, CT): """ return _gsw_ufuncs.spiciness1(SA, CT) +spiciness1.types = _gsw_ufuncs.spiciness1.types +spiciness1 = match_args_return(spiciness1) -@match_args_return def spiciness2(SA, CT): """ Calculates spiciness from Absolute Salinity and Conservative @@ -4114,8 +4320,9 @@ def spiciness2(SA, CT): """ return _gsw_ufuncs.spiciness2(SA, CT) +spiciness2.types = _gsw_ufuncs.spiciness2.types +spiciness2 = match_args_return(spiciness2) -@match_args_return def SR_from_SP(SP): """ Calculates Reference Salinity from Practical Salinity. @@ -4133,8 +4340,9 @@ def SR_from_SP(SP): """ return _gsw_ufuncs.sr_from_sp(SP) +SR_from_SP.types = _gsw_ufuncs.sr_from_sp.types +SR_from_SP = match_args_return(SR_from_SP) -@match_args_return def Sstar_from_SA(SA, p, lon, lat): """ Converts Preformed Salinity from Absolute Salinity. @@ -4158,8 +4366,9 @@ def Sstar_from_SA(SA, p, lon, lat): """ return _gsw_ufuncs.sstar_from_sa(SA, p, lon, lat) +Sstar_from_SA.types = _gsw_ufuncs.sstar_from_sa.types +Sstar_from_SA = match_args_return(Sstar_from_SA) -@match_args_return def Sstar_from_SP(SP, p, lon, lat): """ Calculates Preformed Salinity from Absolute Salinity. @@ -4185,8 +4394,9 @@ def Sstar_from_SP(SP, p, lon, lat): """ return _gsw_ufuncs.sstar_from_sp(SP, p, lon, lat) +Sstar_from_SP.types = _gsw_ufuncs.sstar_from_sp.types +Sstar_from_SP = match_args_return(Sstar_from_SP) -@match_args_return def t_deriv_chem_potential_water_t_exact(SA, t, p): """ Calculates the temperature derivative of the chemical potential of water @@ -4210,8 +4420,9 @@ def t_deriv_chem_potential_water_t_exact(SA, t, p): """ return _gsw_ufuncs.t_deriv_chem_potential_water_t_exact(SA, t, p) +t_deriv_chem_potential_water_t_exact.types = _gsw_ufuncs.t_deriv_chem_potential_water_t_exact.types +t_deriv_chem_potential_water_t_exact = match_args_return(t_deriv_chem_potential_water_t_exact) -@match_args_return def t_freezing(SA, p, saturation_fraction): """ Calculates the in-situ temperature at which seawater freezes. The @@ -4238,8 +4449,9 @@ def t_freezing(SA, p, saturation_fraction): """ return _gsw_ufuncs.t_freezing(SA, p, saturation_fraction) +t_freezing.types = _gsw_ufuncs.t_freezing.types +t_freezing = match_args_return(t_freezing) -@match_args_return def t_freezing_first_derivatives(SA, p, saturation_fraction): """ Calculates the first derivatives of the in-situ temperature at which @@ -4271,8 +4483,9 @@ def t_freezing_first_derivatives(SA, p, saturation_fraction): """ return _gsw_ufuncs.t_freezing_first_derivatives(SA, p, saturation_fraction) +t_freezing_first_derivatives.types = _gsw_ufuncs.t_freezing_first_derivatives.types +t_freezing_first_derivatives = match_args_return(t_freezing_first_derivatives) -@match_args_return def t_freezing_first_derivatives_poly(SA, p, saturation_fraction): """ Calculates the first derivatives of the in-situ temperature at which @@ -4304,8 +4517,9 @@ def t_freezing_first_derivatives_poly(SA, p, saturation_fraction): """ return _gsw_ufuncs.t_freezing_first_derivatives_poly(SA, p, saturation_fraction) +t_freezing_first_derivatives_poly.types = _gsw_ufuncs.t_freezing_first_derivatives_poly.types +t_freezing_first_derivatives_poly = match_args_return(t_freezing_first_derivatives_poly) -@match_args_return def t_freezing_poly(SA, p, saturation_fraction): """ Calculates the in-situ temperature at which seawater freezes from a @@ -4329,8 +4543,9 @@ def t_freezing_poly(SA, p, saturation_fraction): """ return _gsw_ufuncs.t_freezing_poly(SA, p, saturation_fraction) +t_freezing_poly.types = _gsw_ufuncs.t_freezing_poly.types +t_freezing_poly = match_args_return(t_freezing_poly) -@match_args_return def t_from_CT(SA, CT, p): """ Calculates in-situ temperature from the Conservative Temperature of @@ -4353,8 +4568,9 @@ def t_from_CT(SA, CT, p): """ return _gsw_ufuncs.t_from_ct(SA, CT, p) +t_from_CT.types = _gsw_ufuncs.t_from_ct.types +t_from_CT = match_args_return(t_from_CT) -@match_args_return def t_from_pt0_ice(pt0_ice, p): """ Calculates in-situ temperature from the potential temperature of ice Ih @@ -4376,8 +4592,9 @@ def t_from_pt0_ice(pt0_ice, p): """ return _gsw_ufuncs.t_from_pt0_ice(pt0_ice, p) +t_from_pt0_ice.types = _gsw_ufuncs.t_from_pt0_ice.types +t_from_pt0_ice = match_args_return(t_from_pt0_ice) -@match_args_return def thermobaric(SA, CT, p): """ Calculates the thermobaric coefficient of seawater with respect to @@ -4403,8 +4620,9 @@ def thermobaric(SA, CT, p): """ return _gsw_ufuncs.thermobaric(SA, CT, p) +thermobaric.types = _gsw_ufuncs.thermobaric.types +thermobaric = match_args_return(thermobaric) -@match_args_return def z_from_p(p, lat, geo_strf_dyn_height, sea_surface_geopotential): """ Calculates height from sea pressure using the computationally-efficient @@ -4435,3 +4653,5 @@ def z_from_p(p, lat, geo_strf_dyn_height, sea_surface_geopotential): """ return _gsw_ufuncs.z_from_p(p, lat, geo_strf_dyn_height, sea_surface_geopotential) +z_from_p.types = _gsw_ufuncs.z_from_p.types +z_from_p = match_args_return(z_from_p) diff --git a/tools/docstring_parts.py b/tools/docstring_parts.py index 6f2b559..7afea1f 100644 --- a/tools/docstring_parts.py +++ b/tools/docstring_parts.py @@ -50,4 +50,31 @@ Note that the reference pressure, p_ref, of geo_strf_dyn_height must be zero (0) dbar.""", sea_surface_geopotential = "geopotential at zero sea pressure, m^2/s^2", +ns = "order of SA derivative, integer in (0, 1, 2)", +nt = "order of t derivative, integer in (0, 1, 2)", +np = "order of p derivative, integer in (0, 1, 2)", ) + +return_overrides = dict( + gibbs = [ + "gibbs : array-like", + " Specific Gibbs energy or its derivatives.", + " The Gibbs energy (when ns = nt = np = 0) has units of J/kg.", + " The Absolute Salinity derivatives are output in units of (J/kg) (g/kg)^(-ns).", + " The temperature derivatives are output in units of (J/kg) (K)^(-nt).", + " The pressure derivatives are output in units of (J/kg) (Pa)^(-np).", + " The mixed derivatives are output in units of (J/kg) (g/kg)^(-ns) (K)^(-nt) (Pa)^(-np).", + " Note: The derivatives are taken with respect to pressure in Pa, not", + " withstanding that the pressure input into this routine is in dbar.", + ], + gibbs_ice = [ + "gibbs_ice : array-like", + " Specific Gibbs energy of ice or its derivatives.", + " The Gibbs energy (when nt = np = 0) has units of J/kg.", + " The temperature derivatives are output in units of (J/kg) (K)^(-nt).", + " The pressure derivatives are output in units of (J/kg) (Pa)^(-np).", + " The mixed derivatives are output in units of (J/kg) (K)^(-nt) (Pa)^(-np).", + " Note. The derivatives are taken with respect to pressure in Pa, not", + " withstanding that the pressure input into this routine is in dbar.", + ] +) \ No newline at end of file diff --git a/tools/docstring_utils.py b/tools/docstring_utils.py index c6c5e98..001bb41 100644 --- a/tools/docstring_utils.py +++ b/tools/docstring_utils.py @@ -38,6 +38,7 @@ def fix_one_output(lines): lines = [] for line in lines_orig: + # Look for lines ending with a units spec in square brackets. match = re.search(r'(.*)\[(.*)\]', line) if match is not None: units = match.group(2).strip() diff --git a/tools/make_wrapped_ufuncs.py b/tools/make_wrapped_ufuncs.py index f300a60..17c32de 100644 --- a/tools/make_wrapped_ufuncs.py +++ b/tools/make_wrapped_ufuncs.py @@ -11,7 +11,7 @@ from matlab_parser import get_complete_sigdict, get_helpdict from c_header_parser import get_signatures, parse_signatures -from docstring_parts import parameters +from docstring_parts import parameters, return_overrides from docstring_utils import (paragraphs, fix_outputs_doc, docstring_from_sections) @@ -27,9 +27,6 @@ 't_freezing_exact', } -# Functions with integer arguments at the start of the argument list. -first_float_dict = {"gibbs": 3, "gibbs_ice": 2} - wrapper_head = ''' """ Auto-generated wrapper for C ufunc extension; do not edit! @@ -40,15 +37,40 @@ ''' +## Alternatives: The first was the original, but it did not provide a way to +# tell the decorator about the signature of the ufunc. The second solved that +# problem, but failed to provide the argument names for the signature in the +# help function and the ipython "?" functionality. + +# wrapper_template = ''' +# @match_args_return +# def %(funcname)s(%(args)s): +# """%(doc)s +# """ +# return _gsw_ufuncs.%(ufuncname)s(%(args)s) +# ''' + +# wrapper_template = """ +# %(funcname)s = match_args_return(_gsw_ufuncs.%(ufuncname)s) +# %(funcname)s.__doc__ = '''%(doc)s +# ''' +# """ + +# Make a Python function with the proper list of arguments; add the 'types' +# attribute for the use of the decorator; then use the decorator in its +# function form. wrapper_template = ''' -@match_args_return def %(funcname)s(%(args)s): """%(doc)s """ return _gsw_ufuncs.%(ufuncname)s(%(args)s) +%(funcname)s.types = _gsw_ufuncs.%(ufuncname)s.types +%(funcname)s = match_args_return(%(funcname)s) ''' + + def get_argnames(ufname): try: msig = Bunch(msigdict[ufname]) @@ -75,6 +97,7 @@ def get_argnames(ufname): return argnames def get_argname_set(): + # This is not currently used internally. argset = set() for ufname in ufunclist: args = get_argnames(ufname) @@ -83,6 +106,7 @@ def get_argname_set(): return argset def get_ufnames_by_arg(): + # This is not currently used internally. argdict = dict() for ufname in ufunclist: args = get_argnames(ufname) @@ -97,6 +121,7 @@ def get_ufnames_by_arg(): def get_outnames(ufname): + # This is currently used only in get_outname_set, which is not used internally. try: msig = Bunch(msigdict[ufname]) except KeyError: @@ -111,6 +136,7 @@ def get_outnames(ufname): return outnames def get_outname_set(): + # This is not currently used internally. argset = set() for ufname in ufunclist: args = get_outnames(ufname) @@ -119,6 +145,7 @@ def get_outname_set(): return argset def get_help_output_dict(): + # This is not currently used internally. out = Bunch() for ufname in ufunclist: msig = msigdict[ufname] @@ -130,6 +157,8 @@ def get_help_output_dict(): else: raw = '' outdoc = [''] + if ufname in return_overrides: + outdoc = return_overrides[ufname] out[ufname] = Bunch(raw=raw, outdoc=outdoc) return out @@ -142,7 +171,6 @@ def uf_wrapper(ufname): subs = dict(ufuncname=ufname, funcname=msig['name'], args=argstr, - first_float=first_float_dict.get(msig['name'], 0), ) helpdict = get_helpdict(msig['path']) @@ -166,6 +194,8 @@ def uf_wrapper(ufname): outdoc = fix_outputs_doc(helpdict['OUTPUT']) else: outdoc = ['None'] + if ufname in return_overrides: + outdoc = return_overrides[ufname] sections['Returns'] = outdoc doc = docstring_from_sections(sections) except KeyError as e: diff --git a/tools/matlab_parser.py b/tools/matlab_parser.py index e077ddc..813763b 100644 --- a/tools/matlab_parser.py +++ b/tools/matlab_parser.py @@ -105,17 +105,27 @@ def get_help_text(fname): return help def help_text_to_dict(help): + """ + Divide the help text into blocks, using headings as delimiters, and return + them as a dictionary with the headings as keys. + """ + # Headings ('USAGE:', 'DESCRIPTION:', etc.) start with all caps and a colon. keypat = r"^([A-Z ]+):(.*)" hdict = dict() started = False for line in help: keyline = re.match(keypat, line) if keyline: + # We found a new heading. if started: + # End the previous block. hdict[key] = blocklines + # Save the name of the block. key = keyline.groups()[0] blocklines = [] started = True + # If there is anything else on the heading line, start the block + # with it. _s = keyline.groups()[1].strip() if _s: blocklines.append(_s) From a9a9ee0ab2d8469de2130e9794f35ad346ab4683 Mon Sep 17 00:00:00 2001 From: Eric Firing Date: Sun, 16 Apr 2023 13:41:50 -1000 Subject: [PATCH 3/4] Add codegen convenience script; update README.txt --- tools/README.txt | 28 ++++++++++++++++++++++------ tools/codegen | 5 +++++ 2 files changed, 27 insertions(+), 6 deletions(-) create mode 100644 tools/codegen diff --git a/tools/README.txt b/tools/README.txt index dd6473d..cd3c504 100644 --- a/tools/README.txt +++ b/tools/README.txt @@ -1,23 +1,29 @@ This subdirectory ('tools') contains modules and scripts for maintaining the code that is autogenerated from the upstream GSW-Matlab and GSW-C repos. The scripts are to be run from -this directory; they may make assumptions about where they -are, and about where upstream repos are. +this directory; they make assumptions about where they +are, and about where upstream repos are. Specifically, it +is assumed that GSW-Matlab, GSW-C, and GSW-Python git repos +are all in the same base directory. Scripts ------- - copy_from_GSW-C.py: copies the relevant .c and .h files from a sibling GSW-C repo, if the latter are newer. +- mat2npz.py: generates an npz file in gsw/tests containing the + test data and check values from the gsw_data_v3_0.mat file + in the sibling GSW-Matlab repo. - make_ufuncs.py: Generates the src/_ufuncs.c file to turn the scalar C functions into numpy ufuncs. It writes ufuncs.list in the current directory as a record of the ufunc names. Functions are identified as ufunc candidates based on their signatures, parsed from src/c_gsw/gswteos-10.h. - make_wrapped_ufuncs.py: Generates gsw/_wrapped_ufuncs.py based on - the output of make_ufuncs.py. -- mat2npz.py: generates an npz file in gsw/tests containing the - test data and check values from the gsw_data_v3_0.mat file - in the sibling GSW-Matlab repo. + the output of make_ufuncs.py. It adds docstrings constructed + from the Matlab help text. +- fix_wrapped_ufunc_typos.py: Fixes docstring typos that have been + identified, but not yet fixed, in the GSW-Matlab repo. +- codegen: Runs the last three python scripts. Modules ------- @@ -32,3 +38,13 @@ Modules This is not used by any of the other functions or scripts, but was used when initially categorizing the functions for inclusion in submodules. + +Notes +----- +- In addition to the generated src/_ufuncs.c, there are two C files + that are hand-written: src/method_bodies.c and src/method_def_entries.c. + These are imported by src/_ufuncs.c. They handle some C functions + that are not suitable for ufuncs. +- Specialized Matlab parsing is also done in gsw/tests/check_functions.py, + which is used by gsw/tests/test_check_functions.py; see the docstring + of the former for more info. \ No newline at end of file diff --git a/tools/codegen b/tools/codegen new file mode 100644 index 0000000..274f24b --- /dev/null +++ b/tools/codegen @@ -0,0 +1,5 @@ +# Source this file to run the code generation scripts in sequence. + +python make_ufuncs.py +python make_wrapped_ufuncs.py +python fix_wrapped_ufunc_typos.py From 35a7e72a2b7c4c858af5083215c7ce4f0dfc5805 Mon Sep 17 00:00:00 2001 From: Eric Firing Date: Sun, 16 Apr 2023 15:40:05 -1000 Subject: [PATCH 4/4] Add test, fix a bug. --- .pre-commit-config.yaml | 3 +- gsw/_fixed_wrapped_ufuncs.py | 2 +- gsw/tests/test_gibbs.py | 86 ++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 gsw/tests/test_gibbs.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d66864e..d6f89cb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,7 +13,8 @@ repos: exclude: > (?x)^( .*\.c| - tools/fix_wrapped_ufunc_typos\.py + tools/fix_wrapped_ufunc_typos\.py| + gsw/tests/test_gibbs\.py )$ args: - --ignore-words-list=nin,preformed,wih, diff --git a/gsw/_fixed_wrapped_ufuncs.py b/gsw/_fixed_wrapped_ufuncs.py index 8ab15c8..4294f87 100644 --- a/gsw/_fixed_wrapped_ufuncs.py +++ b/gsw/_fixed_wrapped_ufuncs.py @@ -33,7 +33,7 @@ def gibbs(ns, nt, np, SA, t, p): def gibbs_ice(nt, np, t, p): params = {"nt": nt, "np": np} for k, v in params.items(): - u = np.unique(v) + u = numpy.unique(v) if u.min() < 0 or u.max() > 2 or u.dtype.kind != "i": raise ValueError("nt, np must contain integers 0, 1, or 2;" f" found {k}={v}") diff --git a/gsw/tests/test_gibbs.py b/gsw/tests/test_gibbs.py new file mode 100644 index 0000000..eb286f5 --- /dev/null +++ b/gsw/tests/test_gibbs.py @@ -0,0 +1,86 @@ +import numpy as np +from numpy.testing import assert_almost_equal + +import gsw + + +def test_gibbs_0(): + SA = np.array([35, 34])[:, np.newaxis] + p = np.array([0, 1000])[np.newaxis, :] + out = gsw.gibbs(0, 0, 0, SA, 15, p) + expected = np.array([[-1624.830331610998, 8102.300078026374], + [-1694.143599586675, 8040.219363411558]]) + assert_almost_equal(out, expected) + + +def test_gibbs_1(): + ns = np.array([0, 1])[:, np.newaxis, np.newaxis] + nt = np.array([0, 1])[np.newaxis, :, np.newaxis] + np_ = np.array([0, 1])[np.newaxis, np.newaxis, :] + out = gsw.gibbs(ns, nt, np_, 35, 15, 0) + expected = np.array([[[-1624.830331610998, 9.748019262990162e-04], + [-213.3508284006898, 2.083180196723266e-07]], + [[70.39427245333731, -7.288938619915955e-07], + [0.590312110989411, 2.083180196723266e-07]]]) + expected[1, 1, 1] = np.nan # Unless we add this case to GSW-C. + print(out) + assert_almost_equal(out, expected) + + +def test_gibbs_2(): + params = [ + (2, 0, 0, 35, 15, 0, 2.144088568168594), + (0, 2, 0, 35, 15, 0, -13.86057508638656), + (0, 0, 2, 35, 15, 0, -4.218331910346273e-13) + ] + for p in params: + assert_almost_equal(gsw.gibbs(*p[:6]), p[6]) + + +def test_gibbs_ice(): + out = gsw.gibbs_ice(1, 0, 0, [0, 100]) + expected = np.array([1220.788661299953, 1220.962914882458]) + assert_almost_equal(out, expected) + + +# Source, on an Intel Mac: +# octave:3> gsw_gibbs(0, 0, 0, 35, 15, 0) +# ans = -1624.830331610998 +# octave:4> gsw_gibbs(0, 0, 0, 35, 15, 1000) +# ans = 8102.300078026374 +# octave:5> gsw_gibbs(0, 0, 0, 34, 15, 0) +# ans = -1694.143599586675 +# octave:6> gsw_gibbs(0, 0, 0, 34, 15, 1000) +# ans = 8040.219363411558 + + +# octave:7> gsw_gibbs(1, 0, 0, 35, 15, 0) +# ans = 70.39427245333731 +# octave:8> gsw_gibbs(0, 1, 0, 35, 15, 0) +# ans = -213.3508284006898 +# octave:9> gsw_gibbs(0, 0, 1, 35, 15, 0) +# ans = 9.748019262990162e-04 + + +# octave:10> gsw_gibbs(2, 0, 0, 35, 15, 0) +# ans = 2.144088568168594 +# octave:11> gsw_gibbs(0, 2, 0, 35, 15, 0) +# ans = -13.86057508638656 +# octave:12> gsw_gibbs(0, 0, 2, 35, 15, 0) +# ans = -4.218331910346273e-13 + +# octave:13> gsw_gibbs(1, 0, 1, 35, 15, 0) +# ans = -7.288938619915955e-07 +# octave:14> gsw_gibbs(1, 1, 0, 35, 15, 0) +# ans = 0.590312110989411 +# octave:15> gsw_gibbs(0, 1, 1, 35, 15, 0) +# ans = 2.083180196723266e-07 +# octave:16> + +# octave:16> gsw_gibbs(1, 1, 1, 35, 15, 0) +# ans = 1.420449745181019e-09 + +# octave:7> gsw_gibbs_ice(1, 0, 0, 0) +# ans = 1220.788661299953 +# octave:8> gsw_gibbs_ice(1, 0, 0, 100) +# ans = 1220.962914882458 \ No newline at end of file