sectionate 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sectionate/__init__.py +6 -0
- sectionate/catalog/Atlantic_transport_arrays.json +348 -0
- sectionate/gridutils.py +95 -0
- sectionate/section.py +786 -0
- sectionate/tests/test_convergent_transport.py +120 -0
- sectionate/tests/test_section.py +92 -0
- sectionate/tests/test_section_class.py +59 -0
- sectionate/tests/test_section_cornercases.py +63 -0
- sectionate/tests/test_utils.py +4 -0
- sectionate/tracers.py +76 -0
- sectionate/transports.py +505 -0
- sectionate/utils.py +173 -0
- sectionate/version.py +3 -0
- sectionate-0.3.0.dist-info/METADATA +44 -0
- sectionate-0.3.0.dist-info/RECORD +17 -0
- sectionate-0.3.0.dist-info/WHEEL +4 -0
- sectionate-0.3.0.dist-info/licenses/LICENSE +674 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import xarray as xr
|
|
3
|
+
import xgcm
|
|
4
|
+
|
|
5
|
+
# define simple xgcm grid
|
|
6
|
+
def initialize_minimal_outer_grid():
|
|
7
|
+
xh = np.array([0.5])
|
|
8
|
+
yh = np.array([0.5])
|
|
9
|
+
xq = np.array([0., 1.])
|
|
10
|
+
yq = np.array([0., 1.])
|
|
11
|
+
|
|
12
|
+
lon, lat = np.meshgrid(xh, yh)
|
|
13
|
+
lon_c, lat_c = np.meshgrid(xq, yq)
|
|
14
|
+
ds = xr.Dataset({}, coords={
|
|
15
|
+
"xh":xr.DataArray(xh, dims=("xh",)),
|
|
16
|
+
"yh":xr.DataArray(yh, dims=("yh",)),
|
|
17
|
+
"xq":xr.DataArray(xq, dims=("xq",)),
|
|
18
|
+
"yq":xr.DataArray(yq, dims=("yq",)),
|
|
19
|
+
"geolon":xr.DataArray(lon, dims=("yh", "xh")),
|
|
20
|
+
"geolat":xr.DataArray(lat, dims=("yh", "xh")),
|
|
21
|
+
"geolon_c":xr.DataArray(lon_c, dims=("yq", "xq",)),
|
|
22
|
+
"geolat_c":xr.DataArray(lat_c, dims=("yq", "xq",))
|
|
23
|
+
})
|
|
24
|
+
coords = {
|
|
25
|
+
'X': {'outer': 'xq', 'center': 'xh'},
|
|
26
|
+
'Y': {'outer': 'yq', 'center': 'yh'}
|
|
27
|
+
}
|
|
28
|
+
boundary = {
|
|
29
|
+
'X': "extend", # Specifying None causes this to default to `periodic` for some reason
|
|
30
|
+
'Y': "extend" # Specifying None causes this to default to `periodic` for some reason
|
|
31
|
+
}
|
|
32
|
+
grid = xgcm.Grid(ds, coords=coords, boundary=boundary, autoparse_metadata=False)
|
|
33
|
+
return grid
|
|
34
|
+
|
|
35
|
+
def test_convergent_transport():
|
|
36
|
+
from sectionate.section import grid_section
|
|
37
|
+
from sectionate.transports import convergent_transport
|
|
38
|
+
grid = initialize_minimal_outer_grid()
|
|
39
|
+
grid._ds['u'] = xr.DataArray(np.array([[1., -np.sqrt(2.)]]), dims=("yh","xq",))
|
|
40
|
+
grid._ds['v'] = xr.DataArray(np.array([[0], [np.pi]]), dims=("yq","xh",))
|
|
41
|
+
|
|
42
|
+
# closed path around the whole square domain
|
|
43
|
+
lonseg = np.array([0, 1, 1, 0, 0])
|
|
44
|
+
latseg = np.array([0, 0, 1, 1, 0])
|
|
45
|
+
i, j, lons, lats = grid_section(grid, lonseg, latseg)
|
|
46
|
+
|
|
47
|
+
conv = convergent_transport(
|
|
48
|
+
grid,
|
|
49
|
+
i,
|
|
50
|
+
j,
|
|
51
|
+
utr="u",
|
|
52
|
+
vtr="v",
|
|
53
|
+
layer=None,
|
|
54
|
+
geometry="cartesian"
|
|
55
|
+
)['conv_mass_transport'].sum().values
|
|
56
|
+
|
|
57
|
+
assert np.isclose(1. + 0. + np.sqrt(2.) - np.pi, conv, rtol=1.e-14)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def initialize_minimal_spherical_grid():
|
|
61
|
+
xq = np.array([0., 120., 240., 360.])
|
|
62
|
+
yq = np.array([-80, 0., 80.])
|
|
63
|
+
xh = np.array([60., 180., 300.])
|
|
64
|
+
yh = np.array([-40., 40.])
|
|
65
|
+
|
|
66
|
+
lon, lat = np.meshgrid(xh, yh)
|
|
67
|
+
lon_c, lat_c = np.meshgrid(xq, yq)
|
|
68
|
+
ds = xr.Dataset({}, coords={
|
|
69
|
+
"xh":xr.DataArray(xh, dims=("xh",)),
|
|
70
|
+
"yh":xr.DataArray(yh, dims=("yh",)),
|
|
71
|
+
"xq":xr.DataArray(xq, dims=("xq",)),
|
|
72
|
+
"yq":xr.DataArray(yq, dims=("yq",)),
|
|
73
|
+
"geolon":xr.DataArray(lon, dims=("yh", "xh")),
|
|
74
|
+
"geolat":xr.DataArray(lat, dims=("yh", "xh")),
|
|
75
|
+
"geolon_c":xr.DataArray(lon_c, dims=("yq", "xq",)),
|
|
76
|
+
"geolat_c":xr.DataArray(lat_c, dims=("yq", "xq",))
|
|
77
|
+
})
|
|
78
|
+
coords = {
|
|
79
|
+
'X': {'outer': 'xq', 'center': 'xh'},
|
|
80
|
+
'Y': {'outer': 'yq', 'center': 'yh'}
|
|
81
|
+
}
|
|
82
|
+
boundary = {
|
|
83
|
+
'X': "periodic",
|
|
84
|
+
'Y': "extend"
|
|
85
|
+
}
|
|
86
|
+
grid = xgcm.Grid(ds, coords=coords, boundary=boundary, autoparse_metadata=False)
|
|
87
|
+
return grid
|
|
88
|
+
|
|
89
|
+
def test_convergent_transport_convention():
|
|
90
|
+
from sectionate.section import grid_section
|
|
91
|
+
from sectionate.transports import convergent_transport
|
|
92
|
+
grid = initialize_minimal_spherical_grid()
|
|
93
|
+
u = np.zeros((grid._ds.yh.size, grid._ds.xq.size))
|
|
94
|
+
v = np.ones((grid._ds.yq.size, grid._ds.xh.size))
|
|
95
|
+
grid._ds['u'] = xr.DataArray(u, dims=("yh", "xq"))
|
|
96
|
+
grid._ds['v'] = xr.DataArray(v, dims=("yq", "xh"))
|
|
97
|
+
|
|
98
|
+
lonseg = np.array([0., -120., -240., -360.])
|
|
99
|
+
latseg = np.array([0., 0., 0., 0.])
|
|
100
|
+
|
|
101
|
+
i, j, lons, lats = grid_section(grid, lonseg, latseg)
|
|
102
|
+
conv = convergent_transport(
|
|
103
|
+
grid,
|
|
104
|
+
i,
|
|
105
|
+
j,
|
|
106
|
+
utr="u",
|
|
107
|
+
vtr="v",
|
|
108
|
+
layer=None
|
|
109
|
+
)['conv_mass_transport'].sum().values
|
|
110
|
+
|
|
111
|
+
conv_rev = convergent_transport(
|
|
112
|
+
grid,
|
|
113
|
+
i[::-1],
|
|
114
|
+
j[::-1],
|
|
115
|
+
utr="u",
|
|
116
|
+
vtr="v",
|
|
117
|
+
layer=None
|
|
118
|
+
)['conv_mass_transport'].sum().values
|
|
119
|
+
|
|
120
|
+
assert np.equal(-3., conv) and np.equal(-3, conv_rev)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import xarray as xr
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# define simple lat-lon grid
|
|
6
|
+
lon, lat = np.meshgrid(np.arange(360), np.arange(-80, 81))
|
|
7
|
+
ds = xr.Dataset()
|
|
8
|
+
ds["lon"] = xr.DataArray(lon, dims=("y", "x"))
|
|
9
|
+
ds["lat"] = xr.DataArray(lat, dims=("y", "x"))
|
|
10
|
+
|
|
11
|
+
def test_distance_on_unit_sphere():
|
|
12
|
+
from sectionate.section import distance_on_unit_sphere
|
|
13
|
+
|
|
14
|
+
# test of few points with unit radius
|
|
15
|
+
d = distance_on_unit_sphere(0, 0, 1.e-20, 0, R=1.)
|
|
16
|
+
assert np.isclose(d, 0., atol=1.e-14)
|
|
17
|
+
d = distance_on_unit_sphere(0, 0, 360, 0, R=1.)
|
|
18
|
+
assert np.isclose(d, 0., atol=1.e-14)
|
|
19
|
+
d = distance_on_unit_sphere(0, 45, 0, -45, R=1.)
|
|
20
|
+
assert np.isclose(d, np.pi/2, atol=1.e-14)
|
|
21
|
+
d = distance_on_unit_sphere(0, 0, 180, 0, R=1.)
|
|
22
|
+
assert np.isclose(d, np.pi, atol=1.e-14)
|
|
23
|
+
d = distance_on_unit_sphere(180, 0, 90, 0, R=1.)
|
|
24
|
+
assert np.isclose(d, np.pi/2, atol=1.e-14)
|
|
25
|
+
d = distance_on_unit_sphere(180, 45, 180, 0, R=1.)
|
|
26
|
+
assert np.isclose(d, np.pi/4, atol=1.e-14)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_find_closest_grid_point():
|
|
30
|
+
from sectionate.section import find_closest_grid_point
|
|
31
|
+
|
|
32
|
+
# check it works with numpy arrays
|
|
33
|
+
i, j = find_closest_grid_point(0, 0, lon, lat)
|
|
34
|
+
assert np.equal(i, 0)
|
|
35
|
+
assert np.equal(j, 80)
|
|
36
|
+
|
|
37
|
+
# and xarray
|
|
38
|
+
i, j = find_closest_grid_point(0, 0, ds["lon"], ds["lat"])
|
|
39
|
+
assert np.equal(i, 0)
|
|
40
|
+
assert np.equal(j, 80)
|
|
41
|
+
|
|
42
|
+
i, j = find_closest_grid_point(180, 80, ds["lon"], ds["lat"])
|
|
43
|
+
assert np.equal(i, 180)
|
|
44
|
+
assert np.equal(j, 160)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_grid_path():
|
|
48
|
+
from sectionate.section import infer_grid_path
|
|
49
|
+
|
|
50
|
+
# test zonal line
|
|
51
|
+
isec, jsec, lonsec, latsec = infer_grid_path(0, 80, 179, 80, lon, lat)
|
|
52
|
+
assert len(isec) == 180
|
|
53
|
+
assert lonsec[0] == 0.0
|
|
54
|
+
assert lonsec[-1] == 179.0
|
|
55
|
+
assert latsec[0] == 0.0
|
|
56
|
+
assert latsec[-1] == 0.0
|
|
57
|
+
|
|
58
|
+
# test merid line
|
|
59
|
+
isec, jsec, lonsec, latsec = infer_grid_path(0, 0, 0, 160, lon, lat)
|
|
60
|
+
assert len(isec) == 161
|
|
61
|
+
assert lonsec[0] == 0.
|
|
62
|
+
assert lonsec[-1] == 0.
|
|
63
|
+
assert latsec[0] == -80.0
|
|
64
|
+
assert latsec[-1] == 80.0
|
|
65
|
+
|
|
66
|
+
# test diagonal
|
|
67
|
+
isec, jsec, lonsec, latsec = infer_grid_path(0, 0, 100, 100, lon, lat)
|
|
68
|
+
assert len(isec) == 201 # expect ni+nj+1 values
|
|
69
|
+
isec, jsec, lonsec, latsec = infer_grid_path(0, 0, 50, 100, lon, lat)
|
|
70
|
+
assert len(isec) == 151 # expect ni+nj+1 values
|
|
71
|
+
isec, jsec, lonsec, latsec = infer_grid_path(10, 10, 100, 50, lon, lat)
|
|
72
|
+
assert len(isec) == 131 # expect ni+nj+1 values
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_infer_grid_path_from_geo():
|
|
76
|
+
from sectionate.section import infer_grid_path_from_geo
|
|
77
|
+
|
|
78
|
+
# test zonal line
|
|
79
|
+
isec, jsec, lonsec, latsec = infer_grid_path_from_geo(0, 0, 179, 0, lon, lat)
|
|
80
|
+
assert len(isec) == 180
|
|
81
|
+
assert lonsec[0] == 0.0
|
|
82
|
+
assert lonsec[-1] == 179.0
|
|
83
|
+
assert latsec[0] == 0.0
|
|
84
|
+
assert latsec[-1] == 0.0
|
|
85
|
+
|
|
86
|
+
# test merid line
|
|
87
|
+
isec, jsec, lonsec, latsec = infer_grid_path_from_geo(180, -80, 180, 80, lon, lat)
|
|
88
|
+
assert len(isec) == 161
|
|
89
|
+
assert lonsec[0] == 180.0
|
|
90
|
+
assert lonsec[-1] == 180.0
|
|
91
|
+
assert latsec[0] == -80.0
|
|
92
|
+
assert latsec[-1] == 80.0
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import xarray as xr
|
|
3
|
+
import xgcm
|
|
4
|
+
|
|
5
|
+
# define simple xgcm grid
|
|
6
|
+
xq = np.array([0., 60, 120, 180, 240, 300., 360.])
|
|
7
|
+
yq = np.array([-80., -40, 0, 40, 80.])
|
|
8
|
+
|
|
9
|
+
lon_c, lat_c = np.meshgrid(xq, yq)
|
|
10
|
+
ds = xr.Dataset({}, coords={
|
|
11
|
+
"xq":xr.DataArray(xq, dims=("xq",)),
|
|
12
|
+
"yq":xr.DataArray(yq, dims=("yq",)),
|
|
13
|
+
"lon_c":xr.DataArray(lon_c, dims=("yq", "xq",)),
|
|
14
|
+
"lat_c":xr.DataArray(lat_c, dims=("yq", "xq",))
|
|
15
|
+
})
|
|
16
|
+
coords = {
|
|
17
|
+
'X': {'outer': 'xq'},
|
|
18
|
+
'Y': {'outer': 'yq'}
|
|
19
|
+
}
|
|
20
|
+
boundary = {
|
|
21
|
+
'X': 'periodic',
|
|
22
|
+
'Y': 'extend'
|
|
23
|
+
}
|
|
24
|
+
grid = xgcm.Grid(ds, coords=coords, boundary=boundary, autoparse_metadata=False)
|
|
25
|
+
|
|
26
|
+
def modequal(a,b):
|
|
27
|
+
return np.equal(np.mod(a, 360.), np.mod(b, 360.))
|
|
28
|
+
|
|
29
|
+
def test_open_gridded_section():
|
|
30
|
+
from sectionate.section import Section, GriddedSection
|
|
31
|
+
lonseg = np.array([0., 120, 120, 0])
|
|
32
|
+
latseg = np.array([-80., -80, 0, 0])
|
|
33
|
+
sec = Section("testsec", (lonseg, latseg))
|
|
34
|
+
sec_gridded = GriddedSection(sec, grid)
|
|
35
|
+
|
|
36
|
+
assert np.all([
|
|
37
|
+
modequal(sec_gridded.i_c, np.array([0, 1, 2, 2, 2, 1, 0])),
|
|
38
|
+
modequal(sec_gridded.j_c, np.array([0, 0, 0, 1, 2, 2, 2])),
|
|
39
|
+
modequal(sec_gridded.lons_c, np.array([0., 60., 120., 120., 120., 60., 0.])),
|
|
40
|
+
modequal(sec_gridded.lats_c, np.array([-80., -80., -80., -40., 0., 0., 0.]))
|
|
41
|
+
])
|
|
42
|
+
|
|
43
|
+
def test_closed_gridded_parent_section():
|
|
44
|
+
from sectionate.section import Section, join_sections, GriddedSection
|
|
45
|
+
lonseg = np.array([ 0., 120, 120, 0, 0])
|
|
46
|
+
latseg = np.array([-80., -80, 0, 0, -80.])
|
|
47
|
+
# Test join_sections and children/parent relationships
|
|
48
|
+
sec1 = Section("sec1", (lonseg[0:3], latseg[0:3]))
|
|
49
|
+
sec2 = Section("sec2", (lonseg[2: ], latseg[2: ]))
|
|
50
|
+
sec = join_sections("sec", sec1, sec2)
|
|
51
|
+
assert isinstance(sec.children["sec1"], Section)
|
|
52
|
+
# Test results from join_section
|
|
53
|
+
sec_gridded = GriddedSection(sec, grid)
|
|
54
|
+
assert np.all([
|
|
55
|
+
modequal(sec_gridded.i_c, np.array([0, 1, 2, 2, 2, 1, 0, 0, 0])),
|
|
56
|
+
modequal(sec_gridded.j_c, np.array([0, 0, 0, 1, 2, 2, 2, 1, 0])),
|
|
57
|
+
modequal(sec_gridded.lons_c, np.array([0., 60., 120., 120., 120., 60., 0., 0., 0.])),
|
|
58
|
+
modequal(sec_gridded.lats_c, np.array([-80., -80., -80., -40., 0., 0., 0., -40., -80.]))
|
|
59
|
+
])
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import xarray as xr
|
|
3
|
+
import xgcm
|
|
4
|
+
|
|
5
|
+
# define simple xgcm grid
|
|
6
|
+
xq = np.array([0., 60, 120, 180, 240, 300., 360.])
|
|
7
|
+
yq = np.array([-80., -40, 0, 40, 80.])
|
|
8
|
+
|
|
9
|
+
lon_c, lat_c = np.meshgrid(xq, yq)
|
|
10
|
+
ds = xr.Dataset({}, coords={
|
|
11
|
+
"xq":xr.DataArray(xq, dims=("xq",)),
|
|
12
|
+
"yq":xr.DataArray(yq, dims=("yq",)),
|
|
13
|
+
"lon_c":xr.DataArray(lon_c, dims=("yq", "xq",)),
|
|
14
|
+
"lat_c":xr.DataArray(lat_c, dims=("yq", "xq",))
|
|
15
|
+
})
|
|
16
|
+
coords = {
|
|
17
|
+
'X': {'outer': 'xq'},
|
|
18
|
+
'Y': {'outer': 'yq'}
|
|
19
|
+
}
|
|
20
|
+
boundary = {
|
|
21
|
+
'X': 'periodic',
|
|
22
|
+
'Y': 'extend'
|
|
23
|
+
}
|
|
24
|
+
grid = xgcm.Grid(ds, coords=coords, boundary=boundary, autoparse_metadata=False)
|
|
25
|
+
|
|
26
|
+
def modequal(a,b):
|
|
27
|
+
return np.equal(np.mod(a, 360.), np.mod(b, 360.))
|
|
28
|
+
|
|
29
|
+
def test_open_grid_section():
|
|
30
|
+
from sectionate.section import grid_section
|
|
31
|
+
lonseg = np.array([0., 120, 120, 0])
|
|
32
|
+
latseg = np.array([-80., -80, 0, 0])
|
|
33
|
+
i, j, lons, lats = grid_section(grid, lonseg, latseg)
|
|
34
|
+
assert np.all([
|
|
35
|
+
modequal(i, np.array([0, 1, 2, 2, 2, 1, 0])),
|
|
36
|
+
modequal(j, np.array([0, 0, 0, 1, 2, 2, 2])),
|
|
37
|
+
modequal(lons, np.array([0., 60., 120., 120., 120., 60., 0.])),
|
|
38
|
+
modequal(lats, np.array([-80., -80., -80., -40., 0., 0., 0.]))
|
|
39
|
+
])
|
|
40
|
+
|
|
41
|
+
def test_closed_grid_section():
|
|
42
|
+
from sectionate.section import grid_section
|
|
43
|
+
lonseg = np.array([0., 120, 120, 0, 0])
|
|
44
|
+
latseg = np.array([-80., -80, 0, 0, -80.])
|
|
45
|
+
i, j, lons, lats = grid_section(grid, lonseg, latseg)
|
|
46
|
+
assert np.all([
|
|
47
|
+
modequal(i, np.array([0, 1, 2, 2, 2, 1, 0, 0, 0])),
|
|
48
|
+
modequal(j, np.array([0, 0, 0, 1, 2, 2, 2, 1, 0])),
|
|
49
|
+
modequal(lons, np.array([0., 60., 120., 120., 120., 60., 0., 0., 0.])),
|
|
50
|
+
modequal(lats, np.array([-80., -80., -80., -40., 0., 0., 0., -40., -80.]))
|
|
51
|
+
])
|
|
52
|
+
|
|
53
|
+
def test_periodic_grid_section():
|
|
54
|
+
from sectionate.section import grid_section
|
|
55
|
+
lonseg = np.array([300, 60])
|
|
56
|
+
latseg = np.array([0, 0])
|
|
57
|
+
i, j, lons, lats = grid_section(grid, lonseg, latseg)
|
|
58
|
+
assert np.all([
|
|
59
|
+
modequal(i, np.array([5, 0, 1])),
|
|
60
|
+
modequal(j, np.array([2, 2, 2])),
|
|
61
|
+
modequal(lons, np.array([300., 0., 60.])),
|
|
62
|
+
modequal(lats, np.array([0., 0., 0.]))
|
|
63
|
+
])
|
sectionate/tracers.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import xarray as xr
|
|
3
|
+
from .transports import (
|
|
4
|
+
uvindices_from_qindices
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
from .gridutils import (
|
|
8
|
+
check_symmetric,
|
|
9
|
+
coord_dict
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
def extract_tracer(
|
|
13
|
+
name,
|
|
14
|
+
grid,
|
|
15
|
+
i_c,
|
|
16
|
+
j_c,
|
|
17
|
+
sect_coord="sect"
|
|
18
|
+
):
|
|
19
|
+
"""
|
|
20
|
+
Extract tracer data on cell thickness grid along the grid path
|
|
21
|
+
of (i_c, j_c) for plotting.
|
|
22
|
+
|
|
23
|
+
PARAMETERS:
|
|
24
|
+
-----------
|
|
25
|
+
name:
|
|
26
|
+
name of variable in `grid._ds`
|
|
27
|
+
grid: xgcm.Grid
|
|
28
|
+
grid describing model and containing data
|
|
29
|
+
i_c: int
|
|
30
|
+
vorticity point indices along 'X' dimension
|
|
31
|
+
j_c: int
|
|
32
|
+
vorticity point indices along 'Y' dimension
|
|
33
|
+
sect_coord: str
|
|
34
|
+
Name of the dimension describing along-section data in the output. Default: 'sect'.
|
|
35
|
+
|
|
36
|
+
RETURNS:
|
|
37
|
+
--------
|
|
38
|
+
|
|
39
|
+
xarray.DataArray with data interpolated to the U and V points along the section.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
da=grid._ds[name]
|
|
43
|
+
coords = coord_dict(grid)
|
|
44
|
+
symmetric = check_symmetric(grid)
|
|
45
|
+
|
|
46
|
+
# get indices of UV points from broken line
|
|
47
|
+
uvindices = uvindices_from_qindices(grid, i_c, j_c)
|
|
48
|
+
|
|
49
|
+
section = xr.Dataset()
|
|
50
|
+
section["i"] = xr.DataArray(uvindices["i"], dims=sect_coord)
|
|
51
|
+
section["j"] = xr.DataArray(uvindices["j"], dims=sect_coord)
|
|
52
|
+
section["Umask"] = xr.DataArray(uvindices["var"]=="U", dims=sect_coord)
|
|
53
|
+
section["Vmask"] = xr.DataArray(uvindices["var"]=="V", dims=sect_coord)
|
|
54
|
+
|
|
55
|
+
increment = 1 if symmetric else 0
|
|
56
|
+
usel_left = {coords["X"]["center"]: np.mod(section["i"]-increment , da[coords["X"]["center"]].size),
|
|
57
|
+
coords["Y"]["center"]: np.mod(section["j"] , da[coords["Y"]["center"]].size)}
|
|
58
|
+
usel_right = {coords["X"]["center"]: np.mod(section["i"]-increment+1, da[coords["X"]["center"]].size),
|
|
59
|
+
coords["Y"]["center"]: np.mod(section["j"] , da[coords["Y"]["center"]].size)}
|
|
60
|
+
|
|
61
|
+
vsel_left = {coords["X"]["center"]: np.mod(section["i"] , da[coords["X"]["center"]].size),
|
|
62
|
+
coords["Y"]["center"]: np.mod(section["j"]-increment , da[coords["Y"]["center"]].size)}
|
|
63
|
+
vsel_right = {coords["X"]["center"]: np.mod(section["i"] , da[coords["X"]["center"]].size),
|
|
64
|
+
coords["Y"]["center"]: np.mod(section["j"]-increment+1, da[coords["Y"]["center"]].size)}
|
|
65
|
+
|
|
66
|
+
tracer = sum([
|
|
67
|
+
xr.where(~np.isnan(da.isel(usel_right)), 0.5*da.isel(usel_left), da.isel(usel_left) ).fillna(0.) * section["Umask"],
|
|
68
|
+
xr.where(~np.isnan(da.isel(usel_left )), 0.5*da.isel(usel_right), da.isel(usel_right)).fillna(0.) * section["Umask"],
|
|
69
|
+
xr.where(~np.isnan(da.isel(vsel_right)), 0.5*da.isel(vsel_left), da.isel(vsel_left) ).fillna(0.) * section["Vmask"],
|
|
70
|
+
xr.where(~np.isnan(da.isel(vsel_left )), 0.5*da.isel(vsel_right), da.isel(vsel_right)).fillna(0.) * section["Vmask"],
|
|
71
|
+
])
|
|
72
|
+
tracer = tracer.where(tracer!=0., np.nan)
|
|
73
|
+
tracer.name = da.name
|
|
74
|
+
tracer.attrs = da.attrs
|
|
75
|
+
|
|
76
|
+
return tracer
|