tobac 1.6.2__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.
- tobac/__init__.py +112 -0
- tobac/analysis/__init__.py +31 -0
- tobac/analysis/cell_analysis.py +628 -0
- tobac/analysis/feature_analysis.py +212 -0
- tobac/analysis/spatial.py +619 -0
- tobac/centerofgravity.py +226 -0
- tobac/feature_detection.py +1758 -0
- tobac/merge_split.py +324 -0
- tobac/plotting.py +2321 -0
- tobac/segmentation/__init__.py +10 -0
- tobac/segmentation/watershed_segmentation.py +1316 -0
- tobac/testing.py +1179 -0
- tobac/tests/segmentation_tests/test_iris_xarray_segmentation.py +0 -0
- tobac/tests/segmentation_tests/test_segmentation.py +1183 -0
- tobac/tests/segmentation_tests/test_segmentation_time_pad.py +104 -0
- tobac/tests/test_analysis_spatial.py +1109 -0
- tobac/tests/test_convert.py +265 -0
- tobac/tests/test_datetime.py +216 -0
- tobac/tests/test_decorators.py +148 -0
- tobac/tests/test_feature_detection.py +1321 -0
- tobac/tests/test_generators.py +273 -0
- tobac/tests/test_import.py +24 -0
- tobac/tests/test_iris_xarray_match_utils.py +244 -0
- tobac/tests/test_merge_split.py +351 -0
- tobac/tests/test_pbc_utils.py +497 -0
- tobac/tests/test_sample_data.py +197 -0
- tobac/tests/test_testing.py +747 -0
- tobac/tests/test_tracking.py +714 -0
- tobac/tests/test_utils.py +650 -0
- tobac/tests/test_utils_bulk_statistics.py +789 -0
- tobac/tests/test_utils_coordinates.py +328 -0
- tobac/tests/test_utils_internal.py +97 -0
- tobac/tests/test_xarray_utils.py +232 -0
- tobac/tracking.py +613 -0
- tobac/utils/__init__.py +27 -0
- tobac/utils/bulk_statistics.py +360 -0
- tobac/utils/datetime.py +184 -0
- tobac/utils/decorators.py +540 -0
- tobac/utils/general.py +753 -0
- tobac/utils/generators.py +87 -0
- tobac/utils/internal/__init__.py +2 -0
- tobac/utils/internal/coordinates.py +430 -0
- tobac/utils/internal/iris_utils.py +462 -0
- tobac/utils/internal/label_props.py +82 -0
- tobac/utils/internal/xarray_utils.py +439 -0
- tobac/utils/mask.py +364 -0
- tobac/utils/periodic_boundaries.py +419 -0
- tobac/wrapper.py +244 -0
- tobac-1.6.2.dist-info/METADATA +154 -0
- tobac-1.6.2.dist-info/RECORD +53 -0
- tobac-1.6.2.dist-info/WHEEL +5 -0
- tobac-1.6.2.dist-info/licenses/LICENSE +29 -0
- tobac-1.6.2.dist-info/top_level.txt +1 -0
tobac/centerofgravity.py
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"""Identify center of gravity and mass for analysis."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import warnings
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def calculate_cog(tracks, mass, mask):
|
|
8
|
+
"""Calculate center of gravity and mass for each tracked cell.
|
|
9
|
+
|
|
10
|
+
Parameters
|
|
11
|
+
----------
|
|
12
|
+
tracks : pandas.DataFrame
|
|
13
|
+
DataFrame containing trajectories of cell centers.
|
|
14
|
+
|
|
15
|
+
mass : iris.cube.Cube
|
|
16
|
+
Cube of quantity (need coordinates 'time',
|
|
17
|
+
'geopotential_height','projection_x_coordinate' and
|
|
18
|
+
'projection_y_coordinate').
|
|
19
|
+
|
|
20
|
+
mask : iris.cube.Cube
|
|
21
|
+
Cube containing mask (int > where belonging to area/volume
|
|
22
|
+
of feature, 0 else).
|
|
23
|
+
|
|
24
|
+
Returns
|
|
25
|
+
-------
|
|
26
|
+
tracks_out : pandas.DataFrame
|
|
27
|
+
Dataframe containing t, x, y, z positions of center of gravity
|
|
28
|
+
and total mass of each tracked cell at each timestep.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from .utils import mask_cube_cell
|
|
32
|
+
from iris import Constraint
|
|
33
|
+
|
|
34
|
+
warnings.warn(
|
|
35
|
+
"calculate_cog is depreciated and will be removed or significantly changed in v2.0.",
|
|
36
|
+
DeprecationWarning,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
logging.info("start calculating centre of gravity for tracked cells")
|
|
40
|
+
|
|
41
|
+
tracks_out = tracks[["time", "frame", "cell", "time_cell"]]
|
|
42
|
+
|
|
43
|
+
for i_row, row in tracks_out.iterrows():
|
|
44
|
+
cell = row["cell"]
|
|
45
|
+
constraint_time = Constraint(time=row["time"])
|
|
46
|
+
mass_i = mass.extract(constraint_time)
|
|
47
|
+
mask_i = mask.extract(constraint_time)
|
|
48
|
+
mass_masked_i = mask_cube_cell(mass_i, mask_i, cell)
|
|
49
|
+
x_M, y_M, z_M, mass_M = center_of_gravity(mass_masked_i)
|
|
50
|
+
tracks_out.loc[i_row, "x_M"] = float(x_M)
|
|
51
|
+
tracks_out.loc[i_row, "y_M"] = float(y_M)
|
|
52
|
+
tracks_out.loc[i_row, "z_M"] = float(z_M)
|
|
53
|
+
tracks_out.loc[i_row, "mass"] = float(mass_M)
|
|
54
|
+
|
|
55
|
+
logging.info("Finished calculating centre of gravity for tracked cells")
|
|
56
|
+
|
|
57
|
+
return tracks_out
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def calculate_cog_untracked(mass, mask):
|
|
61
|
+
"""Calculate center of gravity and mass for untracked domain parts.
|
|
62
|
+
|
|
63
|
+
Parameters
|
|
64
|
+
----------
|
|
65
|
+
mass : iris.cube.Cube
|
|
66
|
+
Cube of quantity (need coordinates 'time',
|
|
67
|
+
'geopotential_height','projection_x_coordinate' and
|
|
68
|
+
'projection_y_coordinate').
|
|
69
|
+
|
|
70
|
+
mask : iris.cube.Cube
|
|
71
|
+
Cube containing mask (int > where belonging to area/volume
|
|
72
|
+
of feature, 0 else).
|
|
73
|
+
|
|
74
|
+
Returns
|
|
75
|
+
-------
|
|
76
|
+
tracks_out : pandas.DataFrame
|
|
77
|
+
Dataframe containing t, x, y, z positions of center of gravity
|
|
78
|
+
and total mass for untracked part of the domain.
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
from pandas import DataFrame
|
|
82
|
+
from .utils import mask_cube_untracked
|
|
83
|
+
from iris import Constraint
|
|
84
|
+
|
|
85
|
+
warnings.warn(
|
|
86
|
+
"calculate_cog_untracked is depreciated and will be removed or significantly changed in v2.0.",
|
|
87
|
+
DeprecationWarning,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
logging.info(
|
|
91
|
+
"start calculating centre of gravity for untracked parts of the domain"
|
|
92
|
+
)
|
|
93
|
+
tracks_out = DataFrame()
|
|
94
|
+
time_coord = mass.coord("time")
|
|
95
|
+
tracks_out["frame"] = range(len(time_coord.points))
|
|
96
|
+
for i_row, row in tracks_out.iterrows():
|
|
97
|
+
time_i = time_coord.units.num2date(time_coord[int(row["frame"])].points[0])
|
|
98
|
+
constraint_time = Constraint(time=time_i)
|
|
99
|
+
mass_i = mass.extract(constraint_time)
|
|
100
|
+
mask_i = mask.extract(constraint_time)
|
|
101
|
+
mass_untracked_i = mask_cube_untracked(mass_i, mask_i)
|
|
102
|
+
x_M, y_M, z_M, mass_M = center_of_gravity(mass_untracked_i)
|
|
103
|
+
tracks_out.loc[i_row, "time"] = time_i
|
|
104
|
+
tracks_out.loc[i_row, "x_M"] = float(x_M)
|
|
105
|
+
tracks_out.loc[i_row, "y_M"] = float(y_M)
|
|
106
|
+
tracks_out.loc[i_row, "z_M"] = float(z_M)
|
|
107
|
+
tracks_out.loc[i_row, "mass"] = float(mass_M)
|
|
108
|
+
|
|
109
|
+
logging.info(
|
|
110
|
+
"Finished calculating centre of gravity for untracked parts of the domain"
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
return tracks_out
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def calculate_cog_domain(mass):
|
|
117
|
+
"""Calculate center of gravity and mass for entire domain.
|
|
118
|
+
|
|
119
|
+
Parameters
|
|
120
|
+
----------
|
|
121
|
+
mass : iris.cube.Cube
|
|
122
|
+
Cube of quantity (need coordinates 'time',
|
|
123
|
+
'geopotential_height','projection_x_coordinate' and
|
|
124
|
+
'projection_y_coordinate').
|
|
125
|
+
|
|
126
|
+
Returns
|
|
127
|
+
-------
|
|
128
|
+
tracks_out : pandas.DataFrame
|
|
129
|
+
Dataframe containing t, x, y, z positions of center of gravity
|
|
130
|
+
and total mass of the entire domain.
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
from pandas import DataFrame
|
|
134
|
+
from iris import Constraint
|
|
135
|
+
|
|
136
|
+
warnings.warn(
|
|
137
|
+
"calculate_cog_domain is depreciated and will be removed or significantly changed in v2.0.",
|
|
138
|
+
DeprecationWarning,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
logging.info("start calculating centre of gravity for entire domain")
|
|
142
|
+
|
|
143
|
+
time_coord = mass.coord("time")
|
|
144
|
+
|
|
145
|
+
tracks_out = DataFrame()
|
|
146
|
+
tracks_out["frame"] = range(len(time_coord.points))
|
|
147
|
+
for i_row, row in tracks_out.iterrows():
|
|
148
|
+
time_i = time_coord.units.num2date(time_coord[int(row["frame"])].points[0])
|
|
149
|
+
constraint_time = Constraint(time=time_i)
|
|
150
|
+
mass_i = mass.extract(constraint_time)
|
|
151
|
+
x_M, y_M, z_M, mass_M = center_of_gravity(mass_i)
|
|
152
|
+
tracks_out.loc[i_row, "time"] = time_i
|
|
153
|
+
tracks_out.loc[i_row, "x_M"] = float(x_M)
|
|
154
|
+
tracks_out.loc[i_row, "y_M"] = float(y_M)
|
|
155
|
+
tracks_out.loc[i_row, "z_M"] = float(z_M)
|
|
156
|
+
tracks_out.loc[i_row, "mass"] = float(mass_M)
|
|
157
|
+
|
|
158
|
+
logging.info("Finished calculating centre of gravity for entire domain")
|
|
159
|
+
|
|
160
|
+
return tracks_out
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def center_of_gravity(cube_in):
|
|
164
|
+
"""Calculate center of gravity and sum of quantity.
|
|
165
|
+
|
|
166
|
+
Parameters
|
|
167
|
+
----------
|
|
168
|
+
cube_in : iris.cube.Cube
|
|
169
|
+
Cube (potentially masked) of quantity (need coordinates
|
|
170
|
+
'geopotential_height','projection_x_coordinate' and
|
|
171
|
+
'projection_y_coordinate').
|
|
172
|
+
|
|
173
|
+
Returns
|
|
174
|
+
-------
|
|
175
|
+
x : float
|
|
176
|
+
X position of center of gravity.
|
|
177
|
+
|
|
178
|
+
y : float
|
|
179
|
+
Y position of center of gravity.
|
|
180
|
+
|
|
181
|
+
z : float
|
|
182
|
+
Z position of center of gravity.
|
|
183
|
+
|
|
184
|
+
variable_sum : float
|
|
185
|
+
Sum of quantity of over unmasked part of the cube.
|
|
186
|
+
"""
|
|
187
|
+
|
|
188
|
+
from iris.analysis import SUM
|
|
189
|
+
import numpy as np
|
|
190
|
+
|
|
191
|
+
warnings.warn(
|
|
192
|
+
"center_of_gravity is depreciated and will be removed or significantly changed in v2.0.",
|
|
193
|
+
DeprecationWarning,
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
cube_sum = cube_in.collapsed(["bottom_top", "south_north", "west_east"], SUM)
|
|
197
|
+
z = cube_in.coord("geopotential_height")
|
|
198
|
+
x = cube_in.coord("projection_x_coordinate")
|
|
199
|
+
y = cube_in.coord("projection_y_coordinate")
|
|
200
|
+
dimensions_collapse = ["model_level_number", "x", "y"]
|
|
201
|
+
for coord in cube_in.coords():
|
|
202
|
+
if coord.ndim > 1 and (
|
|
203
|
+
cube_in.coord_dims(dimensions_collapse[0])[0] in cube_in.coord_dims(coord)
|
|
204
|
+
or cube_in.coord_dims(dimensions_collapse[1])[0]
|
|
205
|
+
in cube_in.coord_dims(coord)
|
|
206
|
+
or cube_in.coord_dims(dimensions_collapse[2])[0]
|
|
207
|
+
in cube_in.coord_dims(coord)
|
|
208
|
+
):
|
|
209
|
+
cube_in.remove_coord(coord.name())
|
|
210
|
+
if cube_sum.data > 0:
|
|
211
|
+
x = (
|
|
212
|
+
(cube_in * x).collapsed(["model_level_number", "x", "y"], SUM) / cube_sum
|
|
213
|
+
).data
|
|
214
|
+
y = (
|
|
215
|
+
(cube_in * y).collapsed(["model_level_number", "x", "y"], SUM) / cube_sum
|
|
216
|
+
).data
|
|
217
|
+
z = (
|
|
218
|
+
(cube_in * z.points).collapsed(["model_level_number", "x", "y"], SUM)
|
|
219
|
+
/ cube_sum
|
|
220
|
+
).data
|
|
221
|
+
else:
|
|
222
|
+
x = np.nan
|
|
223
|
+
y = np.nan
|
|
224
|
+
z = np.nan
|
|
225
|
+
variable_sum = cube_sum.data
|
|
226
|
+
return (x, y, z, variable_sum)
|