Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The budget support for seaduck #118

Merged
merged 25 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions budget/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# To close budget on particles

The pdf in this directory describes, in theory, how to close budget on Lagrangian particles using seaduck. A paper will (hopefully) be published soon on this, and there will be a proper way to cite then.
> We are activatively tidying up the code for budget closure. The process is quite slow because the boy need to prioritize on getting his PhD :P.

The corresponding code exists, but is a lot less tidy than this package, and is therefore in a private repo. If you would like to use this functionality, please email me (Wenrui Jiang) at [email protected]. I am happy to share the code, explain things, and potentially corroborate on your project.
The pdf in this directory describes, in theory, how to close budget on Lagrangian particles using seaduck. A paper will (hopefully) be published soon on this, and there will be a proper way to cite then.

If you want to contribute to the package by helping me make the budget code public, also email me. I don't know how to thank you enough.
The corresponding code exists, but is a lot less tidy than this package, and is therefore in a private repo. If you would like to use this functionality, please email me (Wenrui Jiang) at [email protected]. I am happy to share the code, explain things, and potentially corroborate on your project.

If you want to contribute to the package by helping me make the budget code public, also email me. I don't know how to thank you enough.
2 changes: 1 addition & 1 deletion docs/get_started.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Get started

For a extremely quick start, read this [one minute guide](./one_min_guide.md). Or, learn about how the objects and functions are related to each other [here](./network_of_object.md).
For a extremely quick start, read this [one minute guide](./one_min_guide.ipynb). Or, learn about how the objects and functions are related to each other [here](./network_of_object.md).

If you have questions about any function, you can always go to [API references](./public_api_reference.md). Or you could checkout the following examples. More example can be found [here](ideal_test.md) and [here](ocean_example.md)

Expand Down
4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ module = [
"numba",
"pooch",
"scipy",
"scipy.interpolate"
"scipy.interpolate",
"xgcm",
"zarr"
]

[tool.ruff]
Expand Down
349 changes: 349 additions & 0 deletions seaduck/eulerian_budget.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,349 @@
import numpy as np

Check warning on line 1 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L1

Added line #L1 was not covered by tests

try: # pragma: no cover
import xgcm
except ImportError: # pragma: no cover
pass


def _raise_if_no_xgcm():
try:
import xgcm

Check warning on line 11 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L9-L11

Added lines #L9 - L11 were not covered by tests

xgcm
except ImportError:
raise ImportError(

Check warning on line 15 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L13-L15

Added lines #L13 - L15 were not covered by tests
"The python package xgcm is needed."
"You can install it with:"
"conda install -c xgcm"
)


def create_ecco_grid(ds):
face_connections = {

Check warning on line 23 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L22-L23

Added lines #L22 - L23 were not covered by tests
"face": {
0: {"X": ((12, "Y", False), (3, "X", False)), "Y": (None, (1, "Y", False))},
1: {
"X": ((11, "Y", False), (4, "X", False)),
"Y": ((0, "Y", False), (2, "Y", False)),
},
2: {
"X": ((10, "Y", False), (5, "X", False)),
"Y": ((1, "Y", False), (6, "X", False)),
},
3: {"X": ((0, "X", False), (9, "Y", False)), "Y": (None, (4, "Y", False))},
4: {
"X": ((1, "X", False), (8, "Y", False)),
"Y": ((3, "Y", False), (5, "Y", False)),
},
5: {
"X": ((2, "X", False), (7, "Y", False)),
"Y": ((4, "Y", False), (6, "Y", False)),
},
6: {
"X": ((2, "Y", False), (7, "X", False)),
"Y": ((5, "Y", False), (10, "X", False)),
},
7: {
"X": ((6, "X", False), (8, "X", False)),
"Y": ((5, "X", False), (10, "Y", False)),
},
8: {
"X": ((7, "X", False), (9, "X", False)),
"Y": ((4, "X", False), (11, "Y", False)),
},
9: {"X": ((8, "X", False), None), "Y": ((3, "X", False), (12, "Y", False))},
10: {
"X": ((6, "Y", False), (11, "X", False)),
"Y": ((7, "Y", False), (2, "X", False)),
},
11: {
"X": ((10, "X", False), (12, "X", False)),
"Y": ((8, "Y", False), (1, "X", False)),
},
12: {
"X": ((11, "X", False), None),
"Y": ((9, "Y", False), (0, "X", False)),
},
}
}

grid = xgcm.Grid(

Check warning on line 71 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L71

Added line #L71 was not covered by tests
ds,
periodic=False,
face_connections=face_connections,
coords={
"X": {"center": "X", "left": "Xp1"},
"Y": {"center": "Y", "left": "Yp1"},
"Z": {"center": "Z", "left": "Zl"},
"time": {"center": "time", "inner": "time_midp"},
},
)
return grid

Check warning on line 82 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L82

Added line #L82 was not covered by tests


def hor_div(tub, grid, xfluxname, yfluxname):

Check warning on line 85 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L85

Added line #L85 was not covered by tests
"""Calculate horizontal divergence using xgcm.

Parameters
----------
tub: sd.OceData or xr.Dataset
The dataset to calculate data from
grid: xgcm.Grid
The Grid of the dataset
xfluxname, yfluxname: string
The name of the variables corresponding to the horizontal fluxes
in concentration m^3/s
"""
try:
tub["Vol"]
except KeyError:
tub._add_missing_vol()
xy_diff = grid.diff_2d_vector(

Check warning on line 102 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L98-L102

Added lines #L98 - L102 were not covered by tests
{"X": tub[xfluxname].fillna(0), "Y": tub[yfluxname].fillna(0)},
boundary="fill",
fill_value=0.0,
)
x_diff = xy_diff["X"]
y_diff = xy_diff["Y"]
hConv = -(x_diff + y_diff) / tub["Vol"]
return hConv

Check warning on line 110 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L107-L110

Added lines #L107 - L110 were not covered by tests


def ver_div(tub, grid, zfluxname):

Check warning on line 113 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L113

Added line #L113 was not covered by tests
"""Calculate horizontal divergence using xgcm.

Parameters
----------
tub: sd.OceData or xr.Dataset
The dataset to calculate data from
grid: xgcm.Grid
The Grid of the dataset
xfluxname, yfluxname, zfluxname: string
The name of the variables corresponding to the fluxes
in concentration m^3/s
"""
try:
tub["Vol"]
except KeyError:
tub._add_missing_vol()
vConv = (

Check warning on line 130 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L126-L130

Added lines #L126 - L130 were not covered by tests
grid.diff(tub[zfluxname].fillna(0), "Z", boundary="fill", fill_value=0.0)
/ tub["Vol"]
)
return vConv

Check warning on line 134 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L134

Added line #L134 was not covered by tests


def total_div(tub, grid, xfluxname, yfluxname, zfluxname):

Check warning on line 137 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L137

Added line #L137 was not covered by tests
"""Calculate 3D divergence using xgcm.

Parameters
----------
tub: sd.OceData or xr.Dataset
The dataset to calculate data from
grid: xgcm.Grid
The Grid of the dataset
zfluxname: string
The name of the variables corresponding to the vertical flux
in concentration m^3/s
"""
hConv = hor_div(tub, grid, xfluxname, yfluxname)
vConv = ver_div(tub, grid, zfluxname)
return hConv + vConv

Check warning on line 152 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L150-L152

Added lines #L150 - L152 were not covered by tests


def _slice_corner(array, fc, iy1, iy2, ix1, ix2):
left = np.minimum(ix1, ix2)
righ = np.maximum(ix1, ix2)
down = np.minimum(iy1, iy2)
uppp = np.maximum(iy1, iy2)
return array[..., fc, down : uppp + 1, left : righ + 1]

Check warning on line 160 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L155-L160

Added lines #L155 - L160 were not covered by tests


def _right90(array):
return array[..., ::-1].transpose([0, 2, 1])

Check warning on line 164 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L163-L164

Added lines #L163 - L164 were not covered by tests


def _left90(array):
return array[..., ::-1, :].transpose([0, 2, 1])

Check warning on line 168 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L167-L168

Added lines #L167 - L168 were not covered by tests


def buffer_x_withface(s, face, lm, rm, tp):

Check warning on line 171 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L171

Added line #L171 was not covered by tests
"""Create buffer zone in x direction for one face of an array.

Parameters
----------
s: numpy.ndarray
the center field, the last dimension being X,
the second dimension being face.
face: int
which face to create buffer for.
lm: int
the size of the margin to the left.
rm: int
the size of the margin to the right.
tp: seaduck.Topology
the topology object of the
"""
shape = list(s.shape)
shape.pop(1)
shape[-1] += lm + rm
xbuffer = np.zeros(shape)
xbuffer[..., lm:-rm] = s[:, face]
try:

Check warning on line 193 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L188-L193

Added lines #L188 - L193 were not covered by tests
fc1, iy1, ix1 = tp.ind_moves((face, tp.iymax, 0), [2 for i in range(lm)])
fc2, iy2, ix2 = tp.ind_moves((face, 0, 0), [2])
left = _slice_corner(s, fc1, iy1, iy2, ix1, ix2)
except IndexError:
left = np.zeros_like(xbuffer[..., :lm])

Check warning on line 198 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L195-L198

Added lines #L195 - L198 were not covered by tests

try:

Check warning on line 200 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L200

Added line #L200 was not covered by tests
fc3, iy3, ix3 = tp.ind_moves((face, tp.iymax, tp.ixmax), [3 for i in range(rm)])
fc4, iy4, ix4 = tp.ind_moves((face, 0, tp.ixmax), [3])
righ = _slice_corner(s, fc3, iy3, iy4, ix3, ix4)
except IndexError:
righ = np.zeros_like(xbuffer[..., -rm:])
try:
xbuffer[..., :lm] = left
except ValueError:
xbuffer[..., :lm] = _right90(left)

Check warning on line 209 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L202-L209

Added lines #L202 - L209 were not covered by tests

try:
xbuffer[..., -rm:] = righ
except ValueError:
xbuffer[..., -rm:] = _right90(righ)

Check warning on line 214 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L211-L214

Added lines #L211 - L214 were not covered by tests

return xbuffer

Check warning on line 216 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L216

Added line #L216 was not covered by tests


def buffer_y_withface(s, face, lm, rm, tp):

Check warning on line 219 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L219

Added line #L219 was not covered by tests
"""Create buffer zone in x direction for one face of an array.

Parameters
----------
s: numpy.ndarray
the center field, the last dimension being X,
the second dimension being face.
face: int
which face to create buffer for.
lm: int
the size of the margin to the bottom.
rm: int
the size of the margin to the top.
tp: seaduck.Topology
the topology object of the
"""
shape = list(s.shape)
shape.pop(1)
shape[-2] += lm + rm
ybuffer = np.zeros(shape)
ybuffer[..., lm:-rm, :] = s[:, face]
try:

Check warning on line 241 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L236-L241

Added lines #L236 - L241 were not covered by tests
fc1, iy1, ix1 = tp.ind_moves((face, 0, tp.ixmax), [1 for i in range(lm)])
fc2, iy2, ix2 = tp.ind_moves((face, 0, 0), [1])
left = _slice_corner(s, fc1, iy1, iy2, ix1, ix2)
except IndexError:
left = np.zeros_like(ybuffer[..., :lm, :])

Check warning on line 246 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L243-L246

Added lines #L243 - L246 were not covered by tests

try:

Check warning on line 248 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L248

Added line #L248 was not covered by tests
fc3, iy3, ix3 = tp.ind_moves((face, tp.iymax, tp.ixmax), [0 for i in range(rm)])
fc4, iy4, ix4 = tp.ind_moves((face, tp.iymax, 0), [0])
righ = _slice_corner(s, fc3, iy3, iy4, ix3, ix4)
except IndexError:
righ = np.zeros_like(ybuffer[..., -rm:, :])
try:
ybuffer[..., :lm, :] = left
except ValueError:
ybuffer[..., :lm, :] = _left90(left)

Check warning on line 257 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L250-L257

Added lines #L250 - L257 were not covered by tests

try:
ybuffer[..., -rm:, :] = righ
except ValueError:
ybuffer[..., -rm:, :] = _left90(righ)
return ybuffer

Check warning on line 263 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L259-L263

Added lines #L259 - L263 were not covered by tests


def third_order_upwind_z(s, w):

Check warning on line 266 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L266

Added line #L266 was not covered by tests
"""Get interpolated salinity in the vertical.

for more info, see
https://mitgcm.readthedocs.io/en/latest/algorithm/adv-schemes.html#third-order-upwind-bias-advection

Parameters
----------
s: numpy.ndarray
the tracer field, the first dimension being Z
w: numpy.ndarray
the vertical velocity field, the first dimension being Zl
"""
w[0] = 0
deltas = np.nan_to_num(s[1:] - s[:-1], 0)
Rj = np.zeros_like(s)
Rj[1:] = deltas
Rjp = np.roll(Rj, -1, axis=0)
Rjm = np.roll(Rj, 1, axis=0)
Rjjp = Rjp - Rj
Rjjm = Rj - Rjm

Check warning on line 286 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L279-L286

Added lines #L279 - L286 were not covered by tests

ssum = np.zeros_like(s)
ssum[1:] = s[1:] + s[:-1]
sz = 0.5 * (ssum - 1 / 6 * (Rjjp + Rjjm) - np.sign(w) * 1 / 6 * (Rjjp - Rjjm))
return sz

Check warning on line 291 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L288-L291

Added lines #L288 - L291 were not covered by tests


def third_order_DST_x(xbuffer, u_cfl):

Check warning on line 294 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L294

Added line #L294 was not covered by tests
"""Get interpolated salinity in X.

for more info, see
https://mitgcm.readthedocs.io/en/latest/algorithm/adv-schemes.html#third-order-direct-space-time

Parameters
----------
xbuffer: numpy.ndarray
the tracer field with buffer zone added (lm=2,rm=1), the last dimension being X
u_cfl: numpy.ndarray
the u velocity normalized by grid size in x, in s^-1.
"""
lm = 2
deltas = np.nan_to_num(np.diff(xbuffer, axis=-1), 0)
Rjp = deltas[..., lm:]
Rj = deltas[..., lm - 1 : -1]
Rjm = deltas[..., lm - 2 : -2]

Check warning on line 311 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L307-L311

Added lines #L307 - L311 were not covered by tests

d0 = (2.0 - u_cfl) * (1.0 - u_cfl) / 6
d1 = (1.0 - u_cfl * u_cfl) / 6

Check warning on line 314 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L313-L314

Added lines #L313 - L314 were not covered by tests

sx = 0.5 * (

Check warning on line 316 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L316

Added line #L316 was not covered by tests
(1 + np.sign(u_cfl)) * (xbuffer[..., lm - 1 : -2] + (d0 * Rj + d1 * Rjm))
+ (1 - np.sign(u_cfl)) * (xbuffer[..., lm:-1] - (d0 * Rj + d1 * Rjp))
)
return sx

Check warning on line 320 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L320

Added line #L320 was not covered by tests


def third_order_DST_y(ybuffer, u_cfl):

Check warning on line 323 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L323

Added line #L323 was not covered by tests
"""Get interpolated salinity in Y.

for more info, see
https://mitgcm.readthedocs.io/en/latest/algorithm/adv-schemes.html#third-order-direct-space-time

Parameters
----------
ybuffer: numpy.ndarray
the tracer field with buffer zone added (lm=2,rm=1), the second last dimension being Y
u_cfl: numpy.ndarray
the v velocity normalized by grid size in y, in s^-1.
"""
lm = 2
deltas = np.nan_to_num(np.diff(ybuffer, axis=-2), 0)
Rjp = deltas[..., lm:, :]
Rj = deltas[..., lm - 1 : -1, :]
Rjm = deltas[..., lm - 2 : -2, :]

Check warning on line 340 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L336-L340

Added lines #L336 - L340 were not covered by tests

d0 = (2.0 - u_cfl) * (1.0 - u_cfl) / 6
d1 = (1.0 - u_cfl * u_cfl) / 6

Check warning on line 343 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L342-L343

Added lines #L342 - L343 were not covered by tests

sy = 0.5 * (

Check warning on line 345 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L345

Added line #L345 was not covered by tests
(1 + np.sign(u_cfl)) * (ybuffer[..., lm - 1 : -2, :] + (d0 * Rj + d1 * Rjm))
+ (1 - np.sign(u_cfl)) * (ybuffer[..., lm:-1, :] - (d0 * Rj + d1 * Rjp))
)
return sy

Check warning on line 349 in seaduck/eulerian_budget.py

View check run for this annotation

Codecov / codecov/patch

seaduck/eulerian_budget.py#L349

Added line #L349 was not covered by tests
Loading
Loading