Skip to content

Commit 7fb8e20

Browse files
committed
Add "where" based ufunc masked array decorator
1 parent 42645a7 commit 7fb8e20

File tree

3 files changed

+94
-33
lines changed

3 files changed

+94
-33
lines changed

gsw/_utilities.py

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from functools import wraps
1+
from functools import wraps, reduce
2+
from itertools import chain
23

34
import numpy as np
45

@@ -16,6 +17,66 @@ def masked_to_nan(arg):
1617
else:
1718
return np.asarray(arg, dtype=float)
1819

20+
def masked_array_support(f):
21+
"""Decorator which adds support for np.ma.masked_arrays to the _wrapped_ufuncs
22+
23+
When one or more masked arrays are encountered as arguments or keyword
24+
arguments, the boolean masks are all logical ORed together then logical
25+
NOT is applied to get the ufunc.where parameter.
26+
27+
If no masked arrays are found, the default where argument of True is always
28+
passed into the wrapped function as a kwarg.
29+
30+
If a where keyword argument is present, it will be used instead of the
31+
masked derived value.
32+
33+
All args/kwargs are then passed directly to the wrapped fuction
34+
"""
35+
36+
@wraps(f)
37+
def wrapper(*args, **kwargs):
38+
where = True # this is the default value for the where kwarg for all ufuncs
39+
40+
# the only thing done when a masked array is encountered is to figure out
41+
# the correct thing to set the where argument to
42+
# the order of the args and kwargs is unimportant.
43+
# this logic inspired by how the np.ma wrapped ufuncs work
44+
# https://github.com/numpy/numpy/blob/cafec60a5e28af98fb8798049edd7942720d2d74/numpy/ma/core.py#L1016-L1025
45+
has_masked_args = any(
46+
np.ma.isMaskedArray(arg) for arg in chain(args, kwargs.values())
47+
)
48+
if has_masked_args:
49+
# we want getmask rather than getmaskarray for performance reasons
50+
mask = reduce(
51+
np.logical_or,
52+
(np.ma.getmask(arg) for arg in chain(args, kwargs.values())),
53+
)
54+
where = ~mask
55+
56+
new_kwargs = {"where": where}
57+
new_kwargs.update(
58+
**kwargs
59+
) # allow user override of the where kwarg if they passed it in
60+
61+
ret = f(*args, **new_kwargs)
62+
63+
if has_masked_args:
64+
# I suspect based on __array_priority__ the returned values might
65+
# not be masked arrays with mixed with other array subclasses with
66+
# a higher prioirty
67+
#
68+
# masked_invalid will retain the existing mask and mask
69+
# any new invalid values (if e.g. the result of unmasked inputs
70+
# was nan/inf)
71+
if isinstance(ret, tuple):
72+
return tuple(np.ma.masked_invalid(rv) for rv in ret)
73+
return np.ma.masked_invalid(ret)
74+
75+
return ret
76+
77+
return wrapper
78+
79+
1980
def match_args_return(f):
2081
"""
2182
Decorator for most functions that operate on profile data.

gsw/_wrapped_ufuncs.py

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
"""
55

66
from . import _gsw_ufuncs
7-
from ._utilities import match_args_return
7+
from ._utilities import masked_array_support
88

99

10-
def adiabatic_lapse_rate_from_CT(SA, CT, p):
10+
@masked_array_support
11+
def adiabatic_lapse_rate_from_CT(SA, CT, p, **kwargs):
1112
"""
1213
Calculates the adiabatic lapse rate of sea water from Conservative
1314
Temperature.
@@ -37,11 +38,10 @@ def adiabatic_lapse_rate_from_CT(SA, CT, p):
3738
3839
3940
"""
40-
return _gsw_ufuncs.adiabatic_lapse_rate_from_ct(SA, CT, p)
41-
adiabatic_lapse_rate_from_CT.types = _gsw_ufuncs.adiabatic_lapse_rate_from_ct.types
42-
adiabatic_lapse_rate_from_CT = match_args_return(adiabatic_lapse_rate_from_CT)
41+
return _gsw_ufuncs.adiabatic_lapse_rate_from_ct(SA, CT, p, **kwargs)
4342

44-
def adiabatic_lapse_rate_ice(t, p):
43+
@masked_array_support
44+
def adiabatic_lapse_rate_ice(t, p, **kwargs):
4545
"""
4646
Calculates the adiabatic lapse rate of ice.
4747
@@ -1624,7 +1624,7 @@ def enthalpy_first_derivatives(SA, CT, p):
16241624
(1) h_SA, the derivative with respect to Absolute Salinity at
16251625
constant CT and p, and
16261626
(2) h_CT, derivative with respect to CT at constant SA and p.
1627-
Note that h_P is specific volume (1/rho) it can be calculated by calling
1627+
Note that h_P is specific volume (1/rho) it can be caclulated by calling
16281628
gsw_specvol(SA,CT,p).
16291629
16301630
Parameters
@@ -1692,7 +1692,7 @@ def enthalpy_first_derivatives_CT_exact(SA, CT, p):
16921692
(1) h_SA, the derivative with respect to Absolute Salinity at
16931693
constant CT and p, and
16941694
(2) h_CT, derivative with respect to CT at constant SA and p.
1695-
Note that h_P is specific volume, v, it can be calculated by calling
1695+
Note that h_P is specific volume, v, it can be calulated by calling
16961696
gsw_specvol_CT_exact(SA,CT,p).
16971697
16981698
Parameters
@@ -2333,7 +2333,7 @@ def frazil_properties(SA_bulk, h_bulk, p):
23332333
"""
23342334
Calculates the mass fraction of ice (mass of ice divided by mass of ice
23352335
plus seawater), w_Ih_final, which results from given values of the bulk
2336-
Absolute Salinity, SA_bulk, bulk enthalpy, h_bulk, occurring at pressure
2336+
Absolute Salinity, SA_bulk, bulk enthalpy, h_bulk, occuring at pressure
23372337
p. The final values of Absolute Salinity, SA_final, and Conservative
23382338
Temperature, CT_final, of the interstitial seawater phase are also
23392339
returned. This code assumes that there is no dissolved air in the
@@ -2355,7 +2355,7 @@ def frazil_properties(SA_bulk, h_bulk, p):
23552355
Absolute Salinity of the seawater in the final state,
23562356
whether or not any ice is present.
23572357
CT_final : array-like, deg C
2358-
Conservative Temperature of the seawater in the final
2358+
Conservative Temperature of the seawater in the the final
23592359
state, whether or not any ice is present.
23602360
w_Ih_final : array-like, unitless
23612361
mass fraction of ice in the final seawater-ice mixture.
@@ -2415,11 +2415,11 @@ def frazil_properties_potential(SA_bulk, h_pot_bulk, p):
24152415
Calculates the mass fraction of ice (mass of ice divided by mass of ice
24162416
plus seawater), w_Ih_eq, which results from given values of the bulk
24172417
Absolute Salinity, SA_bulk, bulk potential enthalpy, h_pot_bulk,
2418-
occurring at pressure p. The final equilibrium values of Absolute
2418+
occuring at pressure p. The final equilibrium values of Absolute
24192419
Salinity, SA_eq, and Conservative Temperature, CT_eq, of the
24202420
interstitial seawater phase are also returned. This code assumes that
24212421
there is no dissolved air in the seawater (that is, saturation_fraction
2422-
is assumed to be zero throughout the code).
2422+
is assumed to be zero thoughout the code).
24232423
24242424
Parameters
24252425
----------
@@ -2436,7 +2436,7 @@ def frazil_properties_potential(SA_bulk, h_pot_bulk, p):
24362436
Absolute Salinity of the seawater in the final state,
24372437
whether or not any ice is present.
24382438
CT_final : array-like, deg C
2439-
Conservative Temperature of the seawater in the final
2439+
Conservative Temperature of the seawater in the the final
24402440
state, whether or not any ice is present.
24412441
w_Ih_final : array-like, unitless
24422442
mass fraction of ice in the final seawater-ice mixture.
@@ -2493,11 +2493,11 @@ def frazil_properties_potential_poly(SA_bulk, h_pot_bulk, p):
24932493
Calculates the mass fraction of ice (mass of ice divided by mass of ice
24942494
plus seawater), w_Ih_eq, which results from given values of the bulk
24952495
Absolute Salinity, SA_bulk, bulk potential enthalpy, h_pot_bulk,
2496-
occurring at pressure p. The final equilibrium values of Absolute
2496+
occuring at pressure p. The final equilibrium values of Absolute
24972497
Salinity, SA_eq, and Conservative Temperature, CT_eq, of the
24982498
interstitial seawater phase are also returned. This code assumes that
24992499
there is no dissolved air in the seawater (that is, saturation_fraction
2500-
is assumed to be zero throughout the code).
2500+
is assumed to be zero thoughout the code).
25012501
25022502
Parameters
25032503
----------
@@ -2514,7 +2514,7 @@ def frazil_properties_potential_poly(SA_bulk, h_pot_bulk, p):
25142514
Absolute Salinity of the seawater in the final state,
25152515
whether or not any ice is present.
25162516
CT_final : array-like, deg C
2517-
Conservative Temperature of the seawater in the final
2517+
Conservative Temperature of the seawater in the the final
25182518
state, whether or not any ice is present.
25192519
w_Ih_final : array-like, unitless
25202520
mass fraction of ice in the final seawater-ice mixture.
@@ -2823,8 +2823,8 @@ def gibbs_ice(nt, np, t, p):
28232823

28242824
def gibbs_ice_part_t(t, p):
28252825
"""
2826-
part of the first temperature derivative of Gibbs energy of ice
2827-
that is the output is gibbs_ice(1,0,t,p) + S0
2826+
part of the the first temperature derivative of Gibbs energy of ice
2827+
that is the outout is gibbs_ice(1,0,t,p) + S0
28282828
28292829
Parameters
28302830
----------
@@ -2859,8 +2859,8 @@ def gibbs_ice_part_t(t, p):
28592859

28602860
def gibbs_ice_pt0(pt0):
28612861
"""
2862-
part of the first temperature derivative of Gibbs energy of ice
2863-
that is the output is "gibbs_ice(1,0,pt0,0) + s0"
2862+
part of the the first temperature derivative of Gibbs energy of ice
2863+
that is the outout is "gibbs_ice(1,0,pt0,0) + s0"
28642864
28652865
Parameters
28662866
----------
@@ -3681,7 +3681,7 @@ def melting_ice_into_seawater(SA, CT, p, w_Ih, t_Ih):
36813681
Absolute Salinity of the seawater in the final state,
36823682
whether or not any ice is present.
36833683
CT_final : array-like, deg C
3684-
Conservative Temperature of the seawater in the final
3684+
Conservative Temperature of the seawater in the the final
36853685
state, whether or not any ice is present.
36863686
w_Ih_final : array-like, unitless
36873687
mass fraction of ice in the final seawater-ice mixture.
@@ -3987,10 +3987,10 @@ def melting_seaice_into_seawater(SA, CT, p, w_seaice, SA_seaice, t_seaice):
39873987
-------
39883988
SA_final : array-like, g/kg
39893989
Absolute Salinity of the mixture of the melted sea ice
3990-
(or ice) and the original seawater
3990+
(or ice) and the orignal seawater
39913991
CT_final : array-like, deg C
39923992
Conservative Temperature of the mixture of the melted
3993-
sea ice (or ice) and the original seawater
3993+
sea ice (or ice) and the orignal seawater
39943994
39953995
39963996
Notes
@@ -4836,7 +4836,7 @@ def pt_from_CT(SA, CT):
48364836
Calculates potential temperature (with a reference sea pressure of
48374837
zero dbar) from Conservative Temperature. This function uses 1.5
48384838
iterations through a modified Newton-Raphson (N-R) iterative solution
4839-
procedure, starting from a rational-function-based initial condition
4839+
proceedure, starting from a rational-function-based initial condition
48404840
for both pt and dCT_dpt.
48414841
48424842
Parameters
@@ -5177,7 +5177,7 @@ def rho(SA, CT, p):
51775177

51785178
def rho_alpha_beta(SA, CT, p):
51795179
"""
5180-
Calculates in-situ density, the appropriate thermal expansion coefficient
5180+
Calculates in-situ density, the appropiate thermal expansion coefficient
51815181
and the appropriate saline contraction coefficient of seawater from
51825182
Absolute Salinity and Conservative Temperature. This function uses the
51835183
computationally-efficient expression for specific volume in terms of
@@ -5363,7 +5363,7 @@ def rho_ice(t, p):
53635363
"""
53645364
Calculates in-situ density of ice from in-situ temperature and pressure.
53655365
Note that the output, rho_ice, is density, not density anomaly; that
5366-
is, 1000 kg/m^3 is not subtracted from it.
5366+
is, 1000 kg/m^3 is not subracted from it.
53675367
53685368
Parameters
53695369
----------
@@ -5548,7 +5548,7 @@ def rho_t_exact(SA, t, p):
55485548
"""
55495549
Calculates in-situ density of seawater from Absolute Salinity and
55505550
in-situ temperature. Note that the output, rho, is density,
5551-
not density anomaly; that is, 1000 kg/m^3 is not subtracted from it.
5551+
not density anomaly; that is, 1000 kg/m^3 is not subracted from it.
55525552
55535553
Parameters
55545554
----------
@@ -6815,7 +6815,7 @@ def specvol(SA, CT, p):
68156815

68166816
def specvol_alpha_beta(SA, CT, p):
68176817
"""
6818-
Calculates specific volume, the appropriate thermal expansion coefficient
6818+
Calculates specific volume, the appropiate thermal expansion coefficient
68196819
and the appropriate saline contraction coefficient of seawater from
68206820
Absolute Salinity and Conservative Temperature. This function uses the
68216821
computationally-efficient expression for specific volume in terms of
@@ -6879,9 +6879,9 @@ def specvol_anom_standard(SA, CT, p):
68796879
Calculates specific volume anomaly from Absolute Salinity, Conservative
68806880
Temperature and pressure. It uses the computationally-efficient
68816881
expression for specific volume as a function of SA, CT and p (Roquet
6882-
et al., 2015). The reference value to which the anomaly is calculated
6882+
et al., 2015). The reference value to which the anomally is calculated
68836883
has an Absolute Salinity of SSO and Conservative Temperature equal to
6884-
0 degrees C.
6884+
0 degress C.
68856885
68866886
Parameters
68876887
----------
@@ -7720,7 +7720,7 @@ def t_freezing_first_derivatives(SA, p, saturation_fraction):
77207720

77217721
def t_freezing_first_derivatives_poly(SA, p, saturation_fraction):
77227722
"""
7723-
Calculates the first derivatives of the in-situ temperature at which
7723+
Calculates the frist derivatives of the in-situ temperature at which
77247724
seawater freezes with respect to Absolute Salinity SA and pressure P (in
77257725
Pa). These expressions come from differentiating the expression that
77267726
defines the freezing temperature, namely the equality between the

tools/make_wrapped_ufuncs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"""
3333
3434
from . import _gsw_ufuncs
35-
from ._utilities import match_args_return
35+
from ._utilities import masked_array_support
3636
3737
'''
3838

0 commit comments

Comments
 (0)