Skip to content

Commit dbc12b7

Browse files
authored
Merge pull request #35 from djgagne/djgagne
Environment.yml and other compatibility updates
2 parents 19ab4b6 + c166fb8 commit dbc12b7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+185
-186
lines changed

.travis.yml

+5-12
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,14 @@
11
language: python
22
env:
3-
- PYTHON_VERSION=2.7 IPYTHON_KERNEL=python2
4-
- PYTHON_VERSION=3.6 IPYTHON_KERNEL=python3
5-
- PYTHON_VERSION=3.7 IPYTHON_KERNEL=python3
6-
3+
- PYTHON_VERSION=3.8 IPYTHON_KERNEL=python3
74
before_install:
85
- wget -q https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
96
- sh Miniconda3-latest-Linux-x86_64.sh -b -p /home/travis/miniconda
107
- export PATH=/home/travis/miniconda/bin:$PATH
118
install:
12-
- conda create -n testenv --yes -c conda-forge python=$PYTHON_VERSION pip numpy scipy matplotlib
13-
- source activate testenv
14-
- conda install --yes -c conda-forge pygrib scikit-learn scikit-image netcdf4 basemap
15-
- pip install arrow
16-
- pip install pytest
17-
- pip install .
9+
- conda env create --yes -f environment.yml
10+
- source activate hagelslag
1811
script:
19-
- py.test
12+
- pytest
2013
notifications:
21-
email: true
14+
email: true

README.md

+27-6
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ The package contains modules for storm identification and tracking, spatio-tempo
1010
machine learning model training to predict hazard intensity as well as space and time translations.
1111

1212
### Citation
13-
If you employ hagelslag in your research, please acknowledge its use with the following citation:
13+
If you employ hagelslag in your research, please acknowledge its use with the following citations:
14+
15+
Gagne, D. J., A. McGovern, S. E. Haupt, R. A. Sobash, J. K. Williams, M. Xue, 2017: Storm-Based Probabilistic Hail
16+
Forecasting with Machine Learning Applied to Convection-Allowing Ensembles, Wea. Forecasting, 32, 1819-1840.
17+
https://doi.org/10.1175/WAF-D-17-0010.1.
1418

1519
Gagne II, D. J., A. McGovern, N. Snook, R. Sobash, J. Labriola, J. K. Williams, S. E. Haupt, and M. Xue, 2016:
1620
Hagelslag: Scalable object-based severe weather analysis and forecasting. Proceedings of the Sixth Symposium on
@@ -21,7 +25,8 @@ djgagne at ou dot edu.
2125

2226
### Requirements
2327

24-
Hagelslag is compatible with Python 2.7 and 3.5. Hagelslag is easiest to install with the help of the Anaconda Python Distribution, but it should work with other
28+
Hagelslag is compatible with Python 3.6 or newer. Hagelslag is easiest to install with the help of the [Miniconda
29+
Python Distribution](https://docs.conda.io/en/latest/miniconda.html), but it should work with other
2530
Python setups as well. Hagelslag requires the following packages and recommends the following versions:
2631

2732
* numpy >= 1.10
@@ -30,14 +35,30 @@ Python setups as well. Hagelslag requires the following packages and recommends
3035
* scikit-learn >= 0.16
3136
* pandas >= 0.15
3237
* arrow >= 0.8.0
33-
* basemap
38+
* pyproj
3439
* netCDF4-python
40+
* xarray
41+
* jupyter
42+
* ncepgrib2
43+
* pygrib
44+
* cython
45+
* pip
46+
* sphinx
47+
* mock
48+
49+
Install dependencies with the following commands:
50+
```
51+
git clone https://github.com/djgagne/hagelslag.git
52+
cd ~/hagelslag
53+
conda env create -f environment.yml
54+
conda activate hagelslag
55+
```
3556

3657
### Installation
58+
Install the latest version of hagelslag with the following command from the top-level hagelslag directory (where setup.py
59+
is):
60+
`pip install .`
3761

38-
To install hagelslag, enter the top-level directory of the package and run the standard python setup command:
39-
40-
python setup.py install
4162

4263
Hagelslag will install the libraries in site-packages and will also install 3 applications into the `bin` directory
4364
of your Python installation.

config/ncar_rt2020_data.config

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ from datetime import datetime
1010

1111
work_path = "/glade/work/sobash/NSC_objects/"
1212
scratch_path = "/glade/scratch/dgagne/"
13+
#dates = pd.read_csv("/glade/work/")
1314
#dates = pd.read_csv("/glade/work/ahijevyc/share/NSC.dates",
1415
# header=None)[0].astype(str).str.pad(14, side="right",fillchar="0")
1516
#date_index = pd.DatetimeIndex(dates)

config/ncar_storm_data_3km.config

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ config = dict(dates=date_index.to_pydatetime(),
2424
watershed_variable="W_UP_MAX",
2525
ensemble_name="NCARSTORM",
2626
ensemble_members=ensemble_members,
27-
model_path="/glade/p/nmmm0039/3KM_WRF_POST/",
27+
model_path="/glade/p/mmm/parc/sobash/NSC/3KM_WRF_POST_12sec_ts/",
2828
model_watershed_params=(10, 1, 80, 100, 60),
2929
size_filter=12,
3030
gaussian_window=1,
+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#!/usr/bin/env python
2+
from hagelslag.processing.ObjectMatcher import shifted_centroid_distance,closest_distance
3+
from hagelslag.processing.ObjectMatcher import centroid_distance, time_distance
4+
5+
import pandas as pd
6+
import numpy as np
7+
import os, sys
8+
from datetime import datetime
9+
10+
work_path = "/glade/work/dgagne/NSC_data/"
11+
scratch_path = "/glade/scratch/dgagne/"
12+
dates = pd.read_csv("/glade/u/home/dgagne/hagelslag/config/ncar_storm_dates_3km_new.txt",
13+
header=None)[0].astype(str).str.pad(14, side="right",fillchar="0")
14+
date_index = pd.DatetimeIndex(dates)
15+
ensemble_members = ["d01"]
16+
pressure_levels = ["850", "700", "500", "300"]
17+
pres_vars = ["GHT_PL", "T_PL", "TD_PL", "U_PL", "V_PL"]
18+
full_pres_vars = []
19+
for pres_var in pres_vars:
20+
for pressure_level in pressure_levels:
21+
full_pres_vars.append(pres_var + "_" + pressure_level)
22+
REFL_1KM_AGL = {
23+
"name": "REFL_1KM_AGL",
24+
"params": (30, 1, 80, 300, 60),
25+
"object_matcher_params":([shifted_centroid_distance],np.array([1.0]),np.array([24000]))
26+
}
27+
W_UP_MAX = {
28+
"name": "W_UP_MAX",
29+
"params": (10, 1, 80, 300, 60),
30+
"object_matcher_params":([closest_distance,shifted_centroid_distance],np.array([0.9,0.1]),np.array([1,24000]))
31+
}
32+
REFL_COM = {
33+
"name": "REFL_COM",
34+
"params": (40, 1, 80, 300, 50),
35+
"object_matcher_params":([shifted_centroid_distance],np.array([1.0]),np.array([24000]))
36+
}
37+
segmentation_approach = "hyst" # "hyst", "ws", or "ew"
38+
REFL_COM["params"] = (35, 50)
39+
watershed_dict = REFL_COM
40+
watershed_variable = watershed_dict["name"]
41+
output_prefix = work_path + "track_data_nsc_3km_"+watershed_variable+"_"+segmentation_approach
42+
config = dict(dates=date_index.to_pydatetime(),
43+
start_hour=1,
44+
end_hour=35, # Don't go above maximum lead time-1 (35) or diagnostics file for storm_variables won't be found
45+
watershed_variable=watershed_variable,
46+
ensemble_name="NCARSTORM",
47+
ensemble_members=ensemble_members,
48+
model_path="/glade/p/mmm/parc/sobash/NSC/3KM_WRF_POST_12sec_ts/",
49+
segmentation_approach = segmentation_approach,
50+
model_watershed_params=watershed_dict["params"],
51+
size_filter=12,
52+
gaussian_window=0,
53+
#mrms_path= work_path + "mrms_ncar/",
54+
mrms_path=None,
55+
mrms_variable="MESH_Max_60min_00.50",
56+
mrms_watershed_params=(13, 1, 125, 100, 100),
57+
object_matcher_params=watershed_dict["object_matcher_params"],
58+
track_matcher_params=([centroid_distance, time_distance],
59+
np.array([80000, 2])),
60+
storm_variables=["UP_HELI_MAX", "GRPL_MAX", "WSPD10MAX", "W_UP_MAX", "W_DN_MAX",
61+
"RVORT1_MAX", "RVORT5_MAX", "UP_HELI_MAX03", "UP_HELI_MAX01",
62+
"UP_HELI_MIN", "REFL_COM", "REFL_1KM_AGL", "REFD_MAX",
63+
"PSFC", "T2", "Q2", "TD2", "U10", "V10"] + full_pres_vars,
64+
#"UP_HELI_MIN", "HAIL_MAXK1", "HAIL_MAX2D", "HAILCAST_DIAM_MAX",
65+
potential_variables=["SBLCL", "MLLCL", "SBCAPE", "MLCAPE", "MUCAPE", "SBCINH", "MLCINH",
66+
"USHR1", "VSHR1", "USHR6", "VSHR6", "U_BUNK", "V_BUNK",
67+
"SRH03", "SRH01", "PSFC", "T2", "Q2", "TD2", "U10", "V10"],
68+
#"PSFC", "T2", "Q2", "TD2", "U10", "V10"] + full_pres_vars,
69+
future_variables=["REFL_COM", "UP_HELI_MAX", "GRPL_MAX", "HAIL_MAXK1", "UP_HELI_MAX03"],
70+
tendency_variables=[],
71+
shape_variables=["area", "eccentricity", "major_axis_length", "minor_axis_length", "orientation"],
72+
#variable_statistics=["mean", "max", "min", "std",
73+
# "percentile_10", "percentile_25", "percentile_50", "percentile_75", "percentile_90"],
74+
variable_statistics=["mean", "max", "min"],
75+
csv_path = output_prefix + "_csv/",
76+
geojson_path = output_prefix + "_json/",
77+
nc_path = output_prefix + "_nc/",
78+
patch_radius=40,
79+
unique_matches=True,
80+
closest_matches=True,
81+
match_steps=True,
82+
train=False,
83+
single_step=True,
84+
label_type="gamma",
85+
model_map_file="/glade/u/home/dgagne/hagelslag/mapfiles/ncar_storm_map_3km.txt",
86+
mask_file="/glade/u/home/dgagne/hagelslag/mapfiles/ncar_storm_us_mask_3km.nc")
87+
if not os.path.exists(config["csv_path"]):
88+
print("csv_path doesn't exist. Try mkdir",config["csv_path"])
89+
sys.exit(1)
90+
if not os.path.exists(config["nc_path"]):
91+
print("nc_path doesn't exist. Try mkdir",config["nc_path"])
92+
sys.exit(1)
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

demos/obj_tracking.py

+17-62
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,16 @@
1414
from matplotlib.patches import Polygon, PathPatch
1515
from matplotlib.collections import PatchCollection
1616
from datetime import datetime, timedelta
17-
from mpl_toolkits.basemap import Basemap
1817
from scipy.ndimage import gaussian_filter, find_objects
1918
from copy import deepcopy
20-
from mysavfig import mysavfig
2119
import pdb, sys, argparse, os
2220

2321

2422
# In[2]:
2523

26-
from hagelslag.processing import EnhancedWatershed
24+
from hagelslag.processing.EnhancedWatershedSegmenter import EnhancedWatershed
2725
from hagelslag.data import ModelOutput
28-
from hagelslag.processing import ObjectMatcher, shifted_centroid_distance, centroid_distance, closest_distance
26+
from hagelslag.processing.ObjectMatcher import ObjectMatcher, closest_distance
2927
from hagelslag.processing import STObject
3028

3129
parser = argparse.ArgumentParser(description='object tracker')
@@ -36,7 +34,8 @@
3634
parser.add_argument('-v','--verbose', action="store_true", help='print more output. useful for debugging')
3735
args = parser.parse_args()
3836

39-
if args.verbose: print args
37+
if args.verbose:
38+
print(args)
4039

4140
odir = '/glade/p/work/ahijevyc/hagelslag/out/'
4241
model_path = "/glade/scratch/ahijevyc/VSE/"
@@ -105,24 +104,19 @@ def add_grid(m):
105104
# If it does, see if 'field' is a variable.
106105
ncf = Dataset(dfile)
107106
if field in ncf.variables:
108-
print dfile
107+
print(dfile)
109108
model_grid.data.append(ncf.variables[field][0,:,:])
110109
ncf.close()
111110
break
112111
ncf.close()
113112
d += deltat
114113

115-
print model_grid.lon.shape, np.maximum.reduce(model_grid.data).shape # max across time dimension
116-
print model_grid.data[0].max(), model_grid.data[-1].max(), np.maximum.reduce(model_grid.data).max()
114+
print(model_grid.lon.shape, np.maximum.reduce(model_grid.data).shape) # max across time dimension
115+
print(model_grid.data[0].max(), model_grid.data[-1].max(), np.maximum.reduce(model_grid.data).max())
117116

118-
basemap = Basemap(resolution="l",
119-
llcrnrlon=model_grid.lon.min()+5., urcrnrlon=model_grid.lon.max()-.1,
120-
llcrnrlat=model_grid.lat.min()+1.5, urcrnrlat=model_grid.lat.max()-.5,
121-
projection='lcc',lat_1=np.mean(model_grid.lat),
122-
lat_0=np.mean(model_grid.lat),lon_0=np.mean(model_grid.lon))
123117
plt.figure(figsize=(10,8))
124-
add_grid(basemap)
125-
basemap.contourf(model_grid.lon, model_grid.lat,
118+
119+
plt.contourf(model_grid.lon, model_grid.lat,
126120
np.maximum.reduce(model_grid.data), # max across time dimension
127121
levels,
128122
extend="max",
@@ -133,24 +127,23 @@ def add_grid(m):
133127
end_date.strftime("%d %b %Y %H:%M")),
134128
fontweight="bold", fontsize=14)
135129
dtstr = "_"+member+run_date.strftime("_%Y%m%d%H")
136-
ret = mysavfig(odir+"uh_swaths/"+field+"_swaths"+dtstr+".png")
130+
ret = plt.savefig(odir+"uh_swaths/"+field+"_swaths"+dtstr+".png")
137131

138132

139133
def get_forecast_objects(model_grid, ew_params, min_size, gaussian_window):
140134
ew = EnhancedWatershed(*ew_params)
141135
model_objects = []
142-
print "Find model objects Hour:",
136+
print("Find model objects Hour:")
143137
for h in range(int((model_grid.end_date - model_grid.start_date).total_seconds()/deltat.total_seconds())+1):
144-
print h,
138+
print(h)
145139
hour_labels = ew.size_filter(ew.label(gaussian_filter(model_grid.data[h], gaussian_window)), min_size)
146140
obj_slices = find_objects(hour_labels)
147141
num_slices = len(obj_slices)
148142
model_objects.append([])
149143
if num_slices > 0:
150144
fig, ax = plt.subplots()
151-
add_grid(basemap)
152-
t = basemap.contourf(model_grid.lon,model_grid.lat,hour_labels,np.arange(0,num_slices+1)+0.5,extend="max",cmap="Set1",latlon=True,title=str(run_date)+" "+field+" "+str(h))
153-
ret = mysavfig(odir+"enh_watershed_ex/ew{0:02d}.png".format(h))
145+
t = plt.contourf(model_grid.lon,model_grid.lat,hour_labels,np.arange(0,num_slices+1)+0.5,extend="max",cmap="Set1",latlon=True,title=str(run_date)+" "+field+" "+str(h))
146+
ret = plt.savefig(odir+"enh_watershed_ex/ew{0:02d}.png".format(h))
154147
for s, sl in enumerate(obj_slices):
155148
model_objects[-1].append(STObject(model_grid.data[h][sl],
156149
#np.where(hour_labels[sl] > 0, 1, 0),
@@ -178,7 +171,7 @@ def get_forecast_objects(model_grid, ew_params, min_size, gaussian_window):
178171
def track_forecast_objects(input_model_objects, model_grid, object_matcher):
179172
model_objects = deepcopy(input_model_objects)
180173
hours = np.arange(int((model_grid.end_date-model_grid.start_date).total_seconds()/deltat.total_seconds()) + 1)
181-
print "hours = ",hours
174+
print("hours = ",hours)
182175
tracked_model_objects = []
183176
for h in hours:
184177
past_time_objs = []
@@ -189,12 +182,12 @@ def track_forecast_objects(input_model_objects, model_grid, object_matcher):
189182
past_time_objs.append(obj)
190183
# If no objects existed in the last time step, then consider objects in current time step all new
191184
if len(past_time_objs) == 0:
192-
print "time",h, " no objects existed in the last time step. consider objects in current time step all new"
185+
print("time",h, " no objects existed in the last time step. consider objects in current time step all new")
193186
tracked_model_objects.extend(deepcopy(model_objects[h]))
194187
# Match from previous time step with current time step
195188
elif len(past_time_objs) > 0 and len(model_objects[h]) > 0:
196189
assignments = object_matcher.match_objects(past_time_objs, model_objects[h], h - 1, h)
197-
print "assignments:", assignments
190+
print("assignments:", assignments)
198191
unpaired = range(len(model_objects[h]))
199192
for pair in assignments:
200193
past_time_objs[pair[0]].extend(model_objects[h][pair[1]])
@@ -209,42 +202,4 @@ def track_forecast_objects(input_model_objects, model_grid, object_matcher):
209202
# np.array([dist_weight, 1-dist_weight]), np.array([max_distance] * 2))
210203
object_matcher = ObjectMatcher([closest_distance],np.array([1]),np.array([4*model_grid.dx]))
211204

212-
tracked_model_objects = track_forecast_objects(model_objects, model_grid, object_matcher)
213-
color_list = ["violet", "cyan", "blue", "green", "purple", "darkgreen", "teal", "royalblue"]
214-
color_arr = np.tile(color_list, len(tracked_model_objects) / len(color_list) + 1)
215-
fig, ax = plt.subplots(figsize=(11, 8.5))
216-
add_grid(basemap)
217-
basemap.contourf(model_grid.lon,
218-
model_grid.lat,
219-
np.maximum.reduce(model_grid.data),
220-
levels,
221-
extend="max",
222-
cmap="YlOrRd", latlon=True)
223-
plt.colorbar(shrink=0.8,fraction=0.05)
224-
c = 0
225-
for t,tracked_model_object in enumerate(tracked_model_objects):
226-
duration = tracked_model_object.end_time - tracked_model_object.start_time + 1
227-
if duration < args.timethresh: continue
228-
# Draw polygon boundaries
229-
patches = []
230-
for time in tracked_model_object.times:
231-
lon = tracked_model_object.boundary_polygon(time)[0]
232-
lat = tracked_model_object.boundary_polygon(time)[1]
233-
#basemap.plot(lon, lat, color=color_arr[c], latlon=True, lw=0.5)
234-
x, y = basemap(lon,lat)
235-
if len(x) > 2: # if there are only 6 pixels, boundary_polygon may be zero-length. Make sure it has at least 3 points.
236-
patches.append(Polygon(np.transpose([x,y]), closed=True, fill=True))
237-
ax.add_collection(PatchCollection(patches, color=color_arr[c], alpha=0.4))
238-
# Label objects
239-
traj = tracked_model_object.trajectory()
240-
xs, ys = basemap(*traj)
241-
#plt.plot(xs,ys, marker='o', markersize=4, color=color_arr[t], lw=2)
242-
for lon, lat, x, y, time, u, v in zip(traj[0], traj[1], xs,ys,tracked_model_object.times,tracked_model_object.u,tracked_model_object.v):
243-
print "#",t," lon,lat=",lon,lat,"time=",time,"u,v=",u,v
244-
if args.verbose: plt.text(x,y,str(time)+":"+str(t), fontsize=7)
245-
plt.text(x,y,str(time), fontsize=7)
246-
#plt.barbs(x,y,u/model_grid.dx, v/model_grid.dx, length=6, barbcolor=color_arr[t])
247-
c = c+1
248-
plt.title(title_info.get_text(), fontweight="bold", fontsize=14)
249-
ret = mysavfig(odir+"storm_tracks/storm_tracks"+dtstr+"_"+str(params["delta"])+".png")
250205

doc/hagelslag.plot.rst

-22
This file was deleted.

doc/hagelslag.rst

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ Subpackages
88

99
hagelslag.data
1010
hagelslag.evaluation
11-
hagelslag.plot
1211
hagelslag.processing
1312
hagelslag.util
1413

0 commit comments

Comments
 (0)