Skip to content

Commit edd9d38

Browse files
Merge pull request #8 from cassidymwagner/ltt_sfs
New traditional structure functions
2 parents f3f210b + a5202db commit edd9d38

7 files changed

+377
-183
lines changed

oceans_sf/calculate_advection.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,7 @@ def calculate_advection( # noqa: D417
4444
northward_advection) if scalar is not provided, otherwise returns an ndarray
4545
of scalar advection.
4646
"""
47-
if grid_type == "latlon":
48-
xcoords = dx.cumsum()
49-
ycoords = dy.cumsum()
50-
51-
if scalar is not None:
52-
dsdy, dsdx = np.gradient(scalar, xcoords, ycoords, axis=(0, 1))
53-
else:
54-
dudy, dudx = np.gradient(u, xcoords, ycoords, axis=(0, 1))
55-
dvdy, dvdx = np.gradient(v, xcoords, ycoords, axis=(0, 1))
56-
57-
else:
47+
if grid_type == "uniform":
5848
dx = np.abs(x[0] - x[1])
5949
dy = np.abs(y[0] - y[1])
6050

@@ -64,6 +54,16 @@ def calculate_advection( # noqa: D417
6454
dudy, dudx = np.gradient(u, dx, dy, axis=(0, 1))
6555
dvdy, dvdx = np.gradient(v, dx, dy, axis=(0, 1))
6656

57+
else:
58+
xcoords = dx.cumsum()
59+
ycoords = dy.cumsum()
60+
61+
if scalar is not None:
62+
dsdy, dsdx = np.gradient(scalar, xcoords, ycoords, axis=(0, 1))
63+
else:
64+
dudy, dudx = np.gradient(u, xcoords, ycoords, axis=(0, 1))
65+
dvdy, dvdx = np.gradient(v, xcoords, ycoords, axis=(0, 1))
66+
6767
if scalar is not None:
6868
advection = u * dsdx + v * dsdy
6969
else:

oceans_sf/calculate_structure_function.py

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from .shift_array2d import shift_array2d
44

55

6-
def calculate_structure_function( # noqa: D417
6+
def calculate_structure_function( # noqa: D417, C901
77
u,
88
v,
99
adv_e,
@@ -13,8 +13,8 @@ def calculate_structure_function( # noqa: D417
1313
skip_velocity_sf=False,
1414
scalar=None,
1515
adv_scalar=None,
16-
traditional_order=0,
17-
boundary="Periodic",
16+
traditional_type=None,
17+
boundary="periodic-all",
1818
):
1919
"""
2020
Calculate structure function, either advective or traditional.
@@ -45,11 +45,14 @@ def calculate_structure_function( # noqa: D417
4545
Array of scalar values. Defaults to None.
4646
adv_scalar: ndarray, optional
4747
Array of scalar advection values. Defaults to None.
48-
traditional_order: int, optional
49-
Order for calculating traditional non-advective structure functions.
50-
If 0, no traditional structure functions are calculated. Defaults to 0.
48+
traditional_type: list, optional
49+
List of traditional structure function types to calculate.
50+
Accepted types are: "LL", "LLL", "LTT", "LSS". If None,
51+
no traditional structure functions are calculated. Defaults to None.
5152
boundary: str, optional
52-
Boundary condition for shifting arrays. Defaults to "Periodic".
53+
Boundary condition for shifting arrays. Accepted strings
54+
are "periodic-x", "periodic-y", and "periodic-all".
55+
Defaults to "periodic-all".
5356
5457
Returns
5558
-------
@@ -119,20 +122,37 @@ def calculate_structure_function( # noqa: D417
119122
+ (inputs["adv_n_" + direction + "_shift"] - adv_n)
120123
* (inputs["v_" + direction + "_shift"] - v)
121124
)
122-
if traditional_order > 0:
123-
N = traditional_order
124-
SF_dict["SF_trad_velocity_" + direction] = np.nanmean(
125-
(inputs["u_" + direction + "_shift"] - u) ** N
126-
)
125+
if traditional_type is not None:
126+
if any("LL" in t for t in traditional_type):
127+
SF_dict["SF_LL_" + direction] = np.nanmean(
128+
(inputs["u_" + direction + "_shift"] - u) ** 2
129+
)
130+
if any("LLL" in t for t in traditional_type):
131+
SF_dict["SF_LLL_" + direction] = np.nanmean(
132+
(inputs["u_" + direction + "_shift"] - u) ** 3
133+
)
134+
if any("LTT" in t for t in traditional_type):
135+
if direction == "right":
136+
SF_dict["SF_LTT_" + direction] = np.nanmean(
137+
(inputs["u_" + direction + "_shift"] - u)
138+
* (inputs["v_" + direction + "_shift"] - v) ** 2
139+
)
140+
141+
if direction == "down":
142+
SF_dict["SF_LTT_" + direction] = np.nanmean(
143+
(inputs["v_" + direction + "_shift"] - v)
144+
* (inputs["u_" + direction + "_shift"] - u) ** 2
145+
)
127146

128147
if scalar is not None:
129148
SF_dict["SF_scalar_" + direction] = np.nanmean(
130149
(inputs["adv_scalar_" + direction + "_shift"] - adv_scalar)
131150
* (inputs["scalar_" + direction + "_shift"] - scalar)
132151
)
133-
if traditional_order > 0:
134-
N = traditional_order
135-
SF_dict["SF_trad_scalar_" + direction] = np.nanmean(
136-
(inputs["scalar_" + direction + "_shift"] - scalar) ** N
137-
)
152+
if traditional_type is not None:
153+
if any("LSS" in t for t in traditional_type):
154+
SF_dict["SF_LSS_" + direction] = np.nanmean(
155+
(inputs["u_" + direction + "_shift"] - u)
156+
* (inputs["scalar_" + direction + "_shift"] - scalar) ** 2
157+
)
138158
return SF_dict

oceans_sf/generate_structure_functions.py

Lines changed: 90 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ def generate_structure_functions( # noqa: C901, D417
1616
y,
1717
skip_velocity_sf=False,
1818
scalar=None,
19-
traditional_order=0,
19+
traditional_type=None,
2020
dx=None,
2121
dy=None,
22-
boundary="Periodic",
22+
boundary="periodic-all",
2323
even="True",
2424
grid_type="uniform",
2525
nbins=10,
@@ -47,15 +47,17 @@ def generate_structure_functions( # noqa: C901, D417
4747
Defaults to False.
4848
scalar: ndarray, optional
4949
2D array of scalar values. Defaults to None.
50-
traditional_order: int, optional
51-
Order for calculating traditional non-advective structure functions.
52-
If 0, no traditional structure functions are calculated. Defaults to 0.
50+
traditional_type: list, optional
51+
List of traditional structure function types to calculate.
52+
Accepted types are: "LL", "LLL", "LTT", "LSS". If None,
53+
no traditional structure functions are calculated. Defaults to None.
5354
dx: float, optional
5455
Grid spacing in the x-direction. Defaults to None.
5556
dy: float, optional
5657
Grid spacing in the y-direction. Defaults to None.
5758
boundary: str, optional
58-
Boundary condition of the data. Defaults to "Periodic".
59+
Boundary condition of the data. Accepted strings are "periodic-x",
60+
"periodic-y", and "periodic-all". Defaults to "periodic-all".
5961
even: bool, optional
6062
Flag indicating if the grid is evenly spaced. Defaults to True.
6163
grid_type:str, optional
@@ -75,20 +77,30 @@ def generate_structure_functions( # noqa: C901, D417
7577
SF_m = None
7678
SF_z_scalar = None
7779
SF_m_scalar = None
78-
SF_z_trad_velocity = None
79-
SF_m_trad_velocity = None
80-
SF_z_trad_scalar = None
81-
SF_m_trad_scalar = None
8280
adv_E = None
8381
adv_N = None
8482
adv_scalar = None
83+
SF_z_LL = None
84+
SF_m_LL = None
85+
SF_z_LLL = None
86+
SF_m_LLL = None
87+
SF_z_LTT = None
88+
SF_m_LTT = None
89+
SF_z_LSS = None
90+
SF_m_LSS = None
8591

8692
# Define a list of separation distances to iterate over.
8793
# Periodic is half the length since the calculation will wrap the data.
88-
if boundary == "Periodic":
94+
if boundary == "periodic-all":
8995
sep_z = range(1, int(len(x) / 2))
9096
sep_m = range(1, int(len(y) / 2))
91-
else:
97+
elif boundary == "periodic-x":
98+
sep_z = range(1, int(len(x) / 2))
99+
sep_m = range(1, int(len(y) - 1))
100+
elif boundary == "periodic-y":
101+
sep_z = range(1, int(len(x) - 1))
102+
sep_m = range(1, int(len(y) / 2))
103+
elif boundary is None:
92104
sep_z = range(1, int(len(x) - 1))
93105
sep_m = range(1, int(len(y) - 1))
94106

@@ -101,22 +113,33 @@ def generate_structure_functions( # noqa: C901, D417
101113
SF_z = np.zeros(len(sep_z) + 1)
102114
SF_m = np.zeros(len(sep_m) + 1)
103115
adv_E, adv_N = calculate_advection(u, v, x, y, dx, dy, grid_type)
104-
if traditional_order > 0:
105-
SF_z_trad_velocity = np.zeros(len(sep_z) + 1)
106-
SF_m_trad_velocity = np.zeros(len(sep_m) + 1)
116+
if traditional_type is not None:
117+
if any("LL" in t for t in traditional_type):
118+
SF_z_LL = np.zeros(len(sep_z) + 1)
119+
SF_m_LL = np.zeros(len(sep_m) + 1)
120+
if any("LLL" in t for t in traditional_type):
121+
SF_z_LLL = np.zeros(len(sep_z) + 1)
122+
SF_m_LLL = np.zeros(len(sep_m) + 1)
123+
if any("LTT" in t for t in traditional_type):
124+
SF_z_LTT = np.zeros(len(sep_z) + 1)
125+
SF_m_LTT = np.zeros(len(sep_m) + 1)
107126

108127
if scalar is not None:
109128
SF_z_scalar = np.zeros(len(sep_z) + 1)
110129
SF_m_scalar = np.zeros(len(sep_m) + 1)
111130
adv_scalar = calculate_advection(u, v, x, y, dx, dy, grid_type, scalar)
112-
if traditional_order > 0:
113-
SF_z_trad_scalar = np.zeros(len(sep_z) + 1)
114-
SF_m_trad_scalar = np.zeros(len(sep_m) + 1)
131+
if traditional_type is not None:
132+
if any("LSS" in t for t in traditional_type):
133+
SF_z_LSS = np.zeros(len(sep_z) + 1)
134+
SF_m_LSS = np.zeros(len(sep_m) + 1)
115135

116136
# Iterate over separations right and down
117137
for down in sep_m:
118138
right = 1
119-
yroll = shift_array1d(y, shift_by=down, boundary=boundary)
139+
if boundary == "periodic-all" or boundary == "periodic-y":
140+
yroll = shift_array1d(y, shift_by=down, boundary="Periodic")
141+
else:
142+
yroll = shift_array1d(y, shift_by=down, boundary=None)
120143

121144
SF_dicts = calculate_structure_function(
122145
u,
@@ -128,18 +151,24 @@ def generate_structure_functions( # noqa: C901, D417
128151
skip_velocity_sf,
129152
scalar,
130153
adv_scalar,
131-
traditional_order,
154+
traditional_type,
132155
boundary,
133156
)
134157

135158
if skip_velocity_sf is False:
136159
SF_m[down] = SF_dicts["SF_velocity_down"]
137-
if traditional_order > 0:
138-
SF_m_trad_velocity[down] = SF_dicts["SF_trad_velocity_down"]
160+
if traditional_type is not None:
161+
if any("LL" in t for t in traditional_type):
162+
SF_m_LL[down] = SF_dicts["SF_LL_down"]
163+
if any("LLL" in t for t in traditional_type):
164+
SF_m_LLL[down] = SF_dicts["SF_LLL_down"]
165+
if any("LTT" in t for t in traditional_type):
166+
SF_m_LTT[down] = SF_dicts["SF_LTT_down"]
139167
if scalar is not None:
140168
SF_m_scalar[down] = SF_dicts["SF_scalar_down"]
141-
if traditional_order > 0:
142-
SF_m_trad_scalar[down] = SF_dicts["SF_trad_scalar_down"]
169+
if traditional_type is not None:
170+
if any("LSS" in t for t in traditional_type):
171+
SF_m_LSS[down] = SF_dicts["SF_LSS_down"]
143172

144173
# Calculate separation distances in y
145174
tmp, yd[down] = calculate_separation_distances(
@@ -148,7 +177,10 @@ def generate_structure_functions( # noqa: C901, D417
148177

149178
for right in sep_z:
150179
down = 1
151-
xroll = shift_array1d(x, shift_by=right, boundary=boundary)
180+
if boundary == "periodic-all" or boundary == "periodic-x":
181+
xroll = shift_array1d(x, shift_by=right, boundary="Periodic")
182+
else:
183+
xroll = shift_array1d(x, shift_by=right, boundary=None)
152184

153185
SF_dicts = calculate_structure_function(
154186
u,
@@ -160,18 +192,24 @@ def generate_structure_functions( # noqa: C901, D417
160192
skip_velocity_sf,
161193
scalar,
162194
adv_scalar,
163-
traditional_order,
195+
traditional_type,
164196
boundary,
165197
)
166198

167199
if skip_velocity_sf is False:
168200
SF_z[right] = SF_dicts["SF_velocity_right"]
169-
if traditional_order > 0:
170-
SF_z_trad_velocity[right] = SF_dicts["SF_trad_velocity_right"]
201+
if traditional_type is not None:
202+
if any("LL" in t for t in traditional_type):
203+
SF_z_LL[right] = SF_dicts["SF_LL_right"]
204+
if any("LLL" in t for t in traditional_type):
205+
SF_z_LLL[right] = SF_dicts["SF_LLL_right"]
206+
if any("LTT" in t for t in traditional_type):
207+
SF_z_LTT[right] = SF_dicts["SF_LTT_right"]
171208
if scalar is not None:
172209
SF_z_scalar[right] = SF_dicts["SF_scalar_right"]
173-
if traditional_order > 0:
174-
SF_z_trad_scalar[right] = SF_dicts["SF_trad_scalar_right"]
210+
if traditional_type is not None:
211+
if any("LSS" in t for t in traditional_type):
212+
SF_z_LSS[right] = SF_dicts["SF_LSS_right"]
175213

176214
# Calculate separation distances in x
177215
xd[right], tmp = calculate_separation_distances(
@@ -183,15 +221,23 @@ def generate_structure_functions( # noqa: C901, D417
183221
if skip_velocity_sf is False:
184222
xd_bin, SF_z = bin_data(xd, SF_z, nbins)
185223
yd_bin, SF_m = bin_data(yd, SF_m, nbins)
186-
if traditional_order > 0:
187-
xd_bin, SF_z_trad_velocity = bin_data(xd, SF_z_trad_velocity, nbins)
188-
yd_bin, SF_m_trad_velocity = bin_data(yd, SF_m_trad_velocity, nbins)
224+
if traditional_type is not None:
225+
if any("LL" in t for t in traditional_type):
226+
xd_bin, SF_z_LL = bin_data(xd, SF_z_LL, nbins)
227+
yd_bin, SF_m_LL = bin_data(yd, SF_m_LL, nbins)
228+
if any("LLL" in t for t in traditional_type):
229+
xd_bin, SF_z_LLL = bin_data(xd, SF_z_LLL, nbins)
230+
yd_bin, SF_m_LLL = bin_data(yd, SF_m_LLL, nbins)
231+
if any("LTT" in t for t in traditional_type):
232+
xd_bin, SF_z_LTT = bin_data(xd, SF_z_LTT, nbins)
233+
yd_bin, SF_m_LTT = bin_data(yd, SF_m_LTT, nbins)
189234
if scalar is not None:
190235
xd_bin, SF_z_scalar = bin_data(xd, SF_z_scalar, nbins)
191236
yd_bin, SF_m_scalar = bin_data(yd, SF_m_scalar, nbins)
192-
if traditional_order > 0:
193-
xd_bin, SF_z_trad_scalar = bin_data(xd, SF_z_trad_scalar, nbins)
194-
yd_bin, SF_m_trad_scalar = bin_data(yd, SF_m_trad_scalar, nbins)
237+
if traditional_type is not None:
238+
if any("LSS" in t for t in traditional_type):
239+
xd_bin, SF_z_LSS = bin_data(xd, SF_z_LSS, nbins)
240+
yd_bin, SF_m_LSS = bin_data(yd, SF_m_LSS, nbins)
195241
xd = xd_bin
196242
yd = yd_bin
197243

@@ -200,10 +246,14 @@ def generate_structure_functions( # noqa: C901, D417
200246
"SF_advection_velocity_meridional": SF_m,
201247
"SF_advection_scalar_zonal": SF_z_scalar,
202248
"SF_advection_scalar_meridional": SF_m_scalar,
203-
"SF_traditional_velocity_zonal": SF_z_trad_velocity,
204-
"SF_traditional_velocity_meridional": SF_m_trad_velocity,
205-
"SF_traditional_scalar_zonal": SF_z_trad_scalar,
206-
"SF_traditional_scalar_meridional": SF_m_trad_scalar,
249+
"SF_LL_zonal": SF_z_LL,
250+
"SF_LL_meridional": SF_m_LL,
251+
"SF_LLL_zonal": SF_z_LLL,
252+
"SF_LLL_meridional": SF_m_LLL,
253+
"SF_LTT_zonal": SF_z_LTT,
254+
"SF_LTT_meridional": SF_m_LTT,
255+
"SF_LSS_zonal": SF_z_LSS,
256+
"SF_LSS_meridional": SF_m_LSS,
207257
"x-diffs": xd,
208258
"y-diffs": yd,
209259
}

0 commit comments

Comments
 (0)