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

ENH: Add cumulative integration function. #3508

Open
wants to merge 31 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
4151d1c
ENH: Add cumulative integration function.
DWesl May 21, 2024
115415d
STY: Ensure two blank lines between test functions.
DWesl May 21, 2024
9ca6384
BUG,STY: Fix NameError in test
DWesl May 21, 2024
9d9dd0d
TST: Expand cumulative_integrate tests.
DWesl May 23, 2024
8ab2549
STY: Change double quotes to single.
DWesl May 23, 2024
6c1f2a7
BUG: Fix cumulative_integrate axis handling.
DWesl May 23, 2024
27f9fc5
TST,BUG: Fix 2D XArray test for cumulative_integral
DWesl May 23, 2024
08ed4ea
BUG: Fix syntax error in units check
DWesl May 24, 2024
6ae4f66
STY: wrap lines and fix tests.
DWesl May 24, 2024
f5bf47b
TST: Fix test mismatches.
DWesl May 24, 2024
c996909
STY: Sort import ordering.
DWesl May 24, 2024
cf5951d
DOC: Add cumulative_integrate to metpy.calc Mathematical Funtions list.
DWesl May 26, 2024
4af2eb1
ENH: Switch to using scipy.integrate.cumulative_trapezoid for calcula…
DWesl May 27, 2024
5eb45cc
FIX: Pass magnitudes to unit-unaware functions.
DWesl May 27, 2024
609c44a
TST: Fix unit handling.
DWesl May 27, 2024
3b6ac91
TST: Fix unit tests.
DWesl May 27, 2024
c15f4bb
TST,FIX: Fix units in test, the right way this time.
DWesl May 27, 2024
480d5d1
BUG: Add back-up for unitless arguments.
DWesl May 27, 2024
c7e721e
Stop trying to handle unitless NumPy arrays.
DWesl May 27, 2024
25f6bf1
STY: Drop assignment to unused variable
DWesl May 27, 2024
2388547
TST,BUG: Fix pytest.raises decorator
DWesl May 27, 2024
0935278
TST,BUG: Fix type of error in test.
DWesl May 27, 2024
1b5d30b
BUG: Fix exception type to match test.
DWesl May 27, 2024
a5d95e4
DOC: Fix example for cumulative_integrate.
DWesl May 27, 2024
40a7386
DOC,FIX: Fix example output formatting.
DWesl May 27, 2024
a9de575
DOC,FIX: Import XArray for that example.
DWesl May 27, 2024
4d0379b
DOC,FIX: Fix call to cumulative_integrate.
DWesl May 27, 2024
b51682b
Use units for delta, not x, for finding final units.
DWesl May 27, 2024
9b82a80
DOC: Remove example with results differing by OS.
DWesl May 27, 2024
5a663e1
STY: Fix ruff errors.
DWesl May 28, 2024
5d72db3
STY: Fix flake8 errors.
DWesl May 28, 2024
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
41 changes: 41 additions & 0 deletions src/metpy/calc/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -1942,3 +1942,44 @@
for v in variables:
ret.append(v[~mask])
return ret


@exporter.export
@xarray_derivative_wrap
def cumulative_integrate(field, axis=None, x=None, delta=None):
"""Return cumulative integral of field along axis.

Uses trapezoidal rule for integration.

Parameters
----------
field : array-like
Array of values for which to calculate the integral
axis : int or str
The axis along which to integrate. If `field` is an
`np.ndarray` or `pint.Quantity`, must be an integer. Defaults
to zero.
x : array-like, optional
The coordinate values along which to integrate
delta : array-like, optional
Spacing between grid points in `field`.

Examples
--------
>>> cumulative_integrate(np.arange(5))
array([0. , 0.5, 2. , 4.5, 8. ])
>>> cumulative_integrate(xr.DataArray(np.arange(5), {"x": (("x",), np.ones(5), {"units": "m"})}, ("x",), "specific_humidity", {"units": "kg/kg"}), "x")

Check failure on line 1971 in src/metpy/calc/tools.py

View workflow job for this annotation

GitHub Actions / Flake8

[ruff] reported by reviewdog 🐶 E501 Line too long (155 > 95) Raw Output: src/metpy/calc/tools.py:1971:96: E501 Line too long (155 > 95)
DWesl marked this conversation as resolved.
Show resolved Hide resolved
<DataArray dims: {"x": 5}
array([...])
Attrs:
units: kg m/kg
"""
n, axis, delta = _process_deriv_args(f, axis, x, delta)

Check failure on line 1977 in src/metpy/calc/tools.py

View workflow job for this annotation

GitHub Actions / Flake8

[ruff] reported by reviewdog 🐶 F821 Undefined name `f` Raw Output: src/metpy/calc/tools.py:1977:42: F821 Undefined name `f`
DWesl marked this conversation as resolved.
Show resolved Hide resolved
take = make_take(n, axis)

right = np.cumsum(
0.5 * (field[take(slice(1, None))] + field[take(slice(None, -1))]) * delta
)
left = np.zeros_like(field[take(0)])

return concatenate((left, right), axis=axis)
30 changes: 29 additions & 1 deletion tests/calc/test_calc_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
first_derivative, geospatial_gradient, get_layer, get_layer_heights,
gradient, laplacian, lat_lon_grid_deltas, nearest_intersection_idx,
parse_angle, pressure_to_height_std, reduce_point_density,
resample_nn_1d, second_derivative, vector_derivative)
resample_nn_1d, second_derivative, vector_derivative,
cumulative_integrate)
from metpy.calc.tools import (_delete_masked_points, _get_bound_pressure_height,
_greater_or_close, _less_or_close, _next_non_masked_element,
_remove_nans, azimuth_range_to_lat_lon, BASE_DEGREE_MULTIPLIER,
Expand Down Expand Up @@ -1557,3 +1558,30 @@
u, v, longitude=lons, latitude=lats, crs=crs, return_only=return_only)

assert len(ddx) == length


def test_cumulative_integrate_numpy():

Check failure on line 1563 in tests/calc/test_calc_tools.py

View workflow job for this annotation

GitHub Actions / Flake8

[ruff] reported by reviewdog 🐶 D103 Missing docstring in public function Raw Output: tests/calc/test_calc_tools.py:1563:5: D103 Missing docstring in public function
DWesl marked this conversation as resolved.
Show resolved Hide resolved
field = np.arange(5)
integral = cumulative_integrate(field)
assert integral == pytest.approx(np.array([0, 0.5, 2, 4.5, 8]))


def test_cumulative_integrate_pint():

Check failure on line 1569 in tests/calc/test_calc_tools.py

View workflow job for this annotation

GitHub Actions / Flake8

[ruff] reported by reviewdog 🐶 D103 Missing docstring in public function Raw Output: tests/calc/test_calc_tools.py:1569:5: D103 Missing docstring in public function
DWesl marked this conversation as resolved.
Show resolved Hide resolved
field = np.arange(6) * units("kg/m^3")

Check failure on line 1570 in tests/calc/test_calc_tools.py

View workflow job for this annotation

GitHub Actions / Flake8

[ruff] reported by reviewdog 🐶 Q000 [*] Double quotes found but single quotes preferred Raw Output: tests/calc/test_calc_tools.py:1570:34: Q000 [*] Double quotes found but single quotes preferred
delta = np.array([1, 2, 3, 2, 1]) * units("cm")

Check failure on line 1571 in tests/calc/test_calc_tools.py

View workflow job for this annotation

GitHub Actions / Flake8

[ruff] reported by reviewdog 🐶 Q000 [*] Double quotes found but single quotes preferred Raw Output: tests/calc/test_calc_tools.py:1571:47: Q000 [*] Double quotes found but single quotes preferred
integral = cumulative_integrate(field, delta=delta)
assert integral == pytest.approx(
np.array([0, 0.5, 3.5, 11, 18, 22.5]) / 100 * units("kg/m^2")

Check failure on line 1574 in tests/calc/test_calc_tools.py

View workflow job for this annotation

GitHub Actions / Flake8

[ruff] reported by reviewdog 🐶 Q000 [*] Double quotes found but single quotes preferred Raw Output: tests/calc/test_calc_tools.py:1574:61: Q000 [*] Double quotes found but single quotes preferred
)


def test_cumulative_integrate_xarray():

Check failure on line 1578 in tests/calc/test_calc_tools.py

View workflow job for this annotation

GitHub Actions / Flake8

[ruff] reported by reviewdog 🐶 D103 Missing docstring in public function Raw Output: tests/calc/test_calc_tools.py:1578:5: D103 Missing docstring in public function
DWesl marked this conversation as resolved.
Show resolved Hide resolved
field = xr.DataArray(
np.arange(10) / 100,
{"x": (("x",), np.arange(100, 1001, 100), {"units": "hPa"})},

Check failure on line 1581 in tests/calc/test_calc_tools.py

View workflow job for this annotation

GitHub Actions / Flake8

[ruff] reported by reviewdog 🐶 Q000 [*] Double quotes found but single quotes preferred Raw Output: tests/calc/test_calc_tools.py:1581:10: Q000 [*] Double quotes found but single quotes preferred
attrs={"units": "g/kg"}
)
integral = cumulative_integrate(field, "x")
assert integral.values == pytest.approx(
np.array([0, 0.5, 2, 4.5, 8, 12.5, 18, 24.5, 32, 40.5, 50])
)
Loading