Skip to content

Commit 0c6f7e3

Browse files
authored
Merge pull request #44 from nsidc/typing-enhancements
Typing enhancements
2 parents 4c4eb58 + 6ce8068 commit 0c6f7e3

File tree

5 files changed

+196
-87
lines changed

5 files changed

+196
-87
lines changed

antarctica_today/compute_mean_climatology.py

Lines changed: 79 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import datetime
22
import os
33
import pickle
4+
from pathlib import Path
5+
from typing import Dict, Literal, Optional, Tuple, Union
46

57
import numpy
68
import pandas
@@ -26,13 +28,13 @@
2628

2729

2830
def compute_daily_climatology_pixel_averages(
29-
baseline_start_year=1990,
30-
melt_start_mmdd=(10, 1),
31-
baseline_end_year=2020,
32-
melt_end_mmdd=(4, 30),
33-
output_picklefile=daily_melt_averages_picklefile,
34-
verbose=True,
35-
):
31+
baseline_start_year: int = 1990,
32+
melt_start_mmdd: Tuple[int, int] = (10, 1),
33+
baseline_end_year: int = 2020,
34+
melt_end_mmdd: Tuple[int, int] = (4, 30),
35+
output_picklefile: Path = daily_melt_averages_picklefile,
36+
verbose: bool = True,
37+
) -> Tuple[numpy.ndarray, Dict[Tuple[int, int], int]]:
3638
"""Compute fraction of days in the baseline period in which each give pixel melts.
3739
3840
Use the baseline period. Calculate, of the days with data (ignoring
@@ -46,7 +48,7 @@ def compute_daily_climatology_pixel_averages(
4648
melt_array, datetimes_dict = read_model_array_picklefile(resample_melt_codes=True)
4749

4850
# Recode melt array from (-1, 0, 1, 2), to (nan, nan, 0.0, 1.0), and convert to floating-point
49-
melt_array_nan_filled = numpy.array(melt_array, dtype=numpy.float32)
51+
melt_array_nan_filled: numpy.ndarray = numpy.array(melt_array, dtype=numpy.float32)
5052
melt_array_nan_filled[melt_array_nan_filled == -1.0] = numpy.nan
5153
melt_array_nan_filled[melt_array_nan_filled == 0.0] = numpy.nan
5254
melt_array_nan_filled[melt_array_nan_filled == 1.0] = 0.0
@@ -112,14 +114,14 @@ def compute_daily_climatology_pixel_averages(
112114
# baseline_dt_list_day_of_months = numpy.array([dt.day for dt in dt_list_melt_season], dtype=numpy.uint8)
113115

114116
# Generate an empty MxNxT array with
115-
average_melt_array = numpy.zeros(
117+
average_melt_array: numpy.ndarray = numpy.zeros(
116118
(melt_array.shape[0], melt_array.shape[1], len(baseline_filler_dt_list)),
117119
dtype=float,
118120
)
119121

120122
# Now, compute the average odds (0-1) of melt on any given day for any given pixel, over the baseline period.
121123
for i, bdt in enumerate(baseline_filler_dt_list):
122-
bdt_day_mask = numpy.array(
124+
bdt_day_mask: numpy.ndarray = numpy.array(
123125
[
124126
((dt.month == bdt.month) and (dt.day == bdt.day))
125127
for dt in dt_list_melt_season
@@ -175,9 +177,9 @@ def compute_daily_climatology_pixel_averages(
175177

176178

177179
def read_daily_melt_averages_picklefile(
178-
build_picklefile_if_not_present=True,
179-
daily_climatology_picklefile=daily_melt_averages_picklefile,
180-
verbose=True,
180+
build_picklefile_if_not_present: bool = True,
181+
daily_climatology_picklefile: Path = daily_melt_averages_picklefile,
182+
verbose: bool = True,
181183
):
182184
"""Read the daily climatology averages picklefile."""
183185
if not os.path.exists(daily_climatology_picklefile):
@@ -192,18 +194,18 @@ def read_daily_melt_averages_picklefile(
192194

193195
if verbose:
194196
print("Reading", daily_climatology_picklefile)
195-
f = open(daily_climatology_picklefile, "rb")
196-
array, dt_dict = pickle.load(f)
197-
f.close()
197+
198+
with open(daily_climatology_picklefile, "rb") as f:
199+
array, dt_dict = pickle.load(f)
198200

199201
return array, dt_dict
200202

201203

202204
def compute_daily_sum_pixel_averages(
203-
daily_picklefile=daily_melt_averages_picklefile,
204-
sum_picklefile=daily_cumulative_melt_averages_picklefile,
205-
verbose=True,
206-
):
205+
daily_picklefile: Path = daily_melt_averages_picklefile,
206+
sum_picklefile: Path = daily_cumulative_melt_averages_picklefile,
207+
verbose: bool = True,
208+
) -> None:
207209
"""Compute a mean daily cumulative melt-day value for each pixel throughout the melt season.
208210
209211
{(mm,dd):(MxN array of integer melt days)}
@@ -217,7 +219,7 @@ def compute_daily_sum_pixel_averages(
217219
"""
218220
# First, read the daily melt value picklefile.
219221
daily_array, dt_dict = read_daily_melt_averages_picklefile(verbose=verbose)
220-
daily_sum_array = numpy.zeros(daily_array.shape, dtype=numpy.int32)
222+
daily_sum_array: numpy.ndarray = numpy.zeros(daily_array.shape, dtype=numpy.int32)
221223
for dt in dt_dict:
222224
daily_sum_array[:, :, dt_dict[dt]] = numpy.array(
223225
numpy.round(
@@ -228,17 +230,18 @@ def compute_daily_sum_pixel_averages(
228230

229231
if verbose:
230232
print("Writing", sum_picklefile, end="...")
231-
f = open(sum_picklefile, "wb")
232-
pickle.dump((daily_sum_array, dt_dict), f)
233-
f.close()
233+
234+
with open(sum_picklefile, "wb") as f:
235+
pickle.dump((daily_sum_array, dt_dict), f)
236+
234237
if verbose:
235238
print("Done.")
236239

237240

238241
def read_daily_sum_melt_averages_picklefile(
239-
build_picklefile_if_not_present=True,
240-
daily_sum_picklefile=daily_cumulative_melt_averages_picklefile,
241-
verbose=True,
242+
build_picklefile_if_not_present: bool = True,
243+
daily_sum_picklefile: Path = daily_cumulative_melt_averages_picklefile,
244+
verbose: bool = True,
242245
):
243246
"""Read the daily climatology averages picklefile."""
244247
if not os.path.exists(daily_sum_picklefile):
@@ -261,14 +264,14 @@ def read_daily_sum_melt_averages_picklefile(
261264

262265

263266
def create_baseline_climatology_tif(
264-
start_date=datetime.datetime(1990, 10, 1),
265-
end_date=datetime.datetime(2020, 4, 30),
266-
f_out_mean=mean_climatology_geotiff,
267-
f_out_std=std_climatology_geotiff,
268-
round_to_integers=True,
269-
gap_filled=True,
270-
verbose=True,
271-
):
267+
start_date: datetime.datetime = datetime.datetime(1990, 10, 1),
268+
end_date: datetime.datetime = datetime.datetime(2020, 4, 30),
269+
f_out_mean: str = mean_climatology_geotiff,
270+
f_out_std: str = std_climatology_geotiff,
271+
round_to_integers: bool = True,
272+
gap_filled: bool = True,
273+
verbose: bool = True,
274+
) -> numpy.ndarray:
272275
"""Generate a "mean annual melt" map over the baseline period.
273276
274277
The melt year for each season is defined from the (mm,dd) from the "start_date"
@@ -287,7 +290,9 @@ def create_baseline_climatology_tif(
287290
num_years = int((end_date - start_date).days / 365.25)
288291
# print(num_years)
289292

290-
annual_sum_grids = numpy.empty(model_array.shape[0:2] + (num_years,), dtype=int)
293+
annual_sum_grids: numpy.ndarray = numpy.empty(
294+
model_array.shape[0:2] + (num_years,), dtype=int
295+
)
291296

292297
if gap_filled:
293298
model_melt_days = model_array
@@ -306,7 +311,7 @@ def create_baseline_climatology_tif(
306311

307312
# print(i, dt1, dt2)
308313

309-
dates_mask = numpy.array(
314+
dates_mask: numpy.ndarray = numpy.array(
310315
[(dt >= dt1) & (dt <= dt2) for dt in datetimes], dtype=bool
311316
)
312317

@@ -340,17 +345,27 @@ def create_baseline_climatology_tif(
340345

341346

342347
def create_partial_year_melt_anomaly_tif(
343-
current_datetime=None, dest_fname=None, gap_filled=True, verbose=True
344-
):
345-
"""Create a tif of melt anomlay compared to baseline climatology for that day of the melt season."""
348+
current_datetime: Optional[datetime.datetime] = None,
349+
dest_fname: Optional[str] = None,
350+
gap_filled: bool = True,
351+
verbose: bool = True,
352+
) -> numpy.ndarray:
353+
"""Create a tif of melt anomaly compared to baseline climatology for that day of the melt season."""
346354
# If no datetime is given, use "today"
347355
if current_datetime is None:
348356
now = datetime.datetime.today()
349-
# Strip of the hour,min,second
357+
# Strip off the hour,min,second
358+
# TODO: Why not use a datetime.date object instead?
350359
current_datetime = datetime.datetime(
351360
year=now.year, month=now.month, day=now.day
352361
)
353362

363+
if not isinstance(current_datetime, datetime.datetime):
364+
raise ValueError(
365+
f"Unexpected value for current_datetime: {current_datetime}."
366+
"This should never happen, but this helps the typechecker narrow."
367+
)
368+
354369
daily_melt_sums, daily_sums_dt_dict = read_daily_sum_melt_averages_picklefile()
355370

356371
first_mmdd_of_melt_season = list(daily_sums_dt_dict.keys())[0]
@@ -376,7 +391,7 @@ def create_partial_year_melt_anomaly_tif(
376391
)
377392

378393
dt_list = sorted(list(dt_dict.keys()))
379-
dt_mask = numpy.array(
394+
dt_mask: numpy.ndarray = numpy.array(
380395
[
381396
((dt >= first_dt_of_present_melt_season) and (dt <= current_datetime))
382397
for dt in dt_list
@@ -413,8 +428,9 @@ def create_partial_year_melt_anomaly_tif(
413428
anomaly_this_season_so_far[ice_mask == 0] = -999
414429

415430
# Round to integers, if it isn't already.
416-
anomalies_int = numpy.array(
417-
numpy.round(anomaly_this_season_so_far), dtype=numpy.int32
431+
anomalies_int: numpy.ndarray = numpy.array(
432+
numpy.round(anomaly_this_season_so_far),
433+
dtype=numpy.int32,
418434
)
419435

420436
# If dest_fname is None, create it.
@@ -434,7 +450,11 @@ def create_partial_year_melt_anomaly_tif(
434450

435451

436452
def create_annual_melt_anomaly_tif(
437-
year, year_melt_tif=None, baseline_melt_tif=None, gap_filled=True, verbose=True
453+
year: int,
454+
year_melt_tif: Optional[str] = None,
455+
baseline_melt_tif: Optional[str] = None,
456+
gap_filled: bool = True,
457+
verbose: bool = True,
438458
):
439459
"""Create a tif of annual melt anomaly compared to baseline climatology.
440460
@@ -522,7 +542,11 @@ def get_baseline_climatology_array(fname=None, gap_filled=True):
522542
return create_baseline_climatology_tif(gap_filled=gap_filled)
523543

524544

525-
def get_annual_melt_sum_array(year, fname=None, gap_filled=True):
545+
def get_annual_melt_sum_array(
546+
year: int,
547+
fname: Optional[str] = None,
548+
gap_filled: bool = True,
549+
):
526550
"""Retrieve the melt year array from the tif.
527551
528552
If it's not available, create it and write the file, then return it.
@@ -545,13 +569,13 @@ def get_annual_melt_sum_array(year, fname=None, gap_filled=True):
545569

546570

547571
def create_annual_melt_sum_tif(
548-
year="all",
549-
output_tif=None,
550-
melt_start_mmdd=(10, 1),
551-
melt_end_mmdd=(4, 30),
552-
gap_filled=True,
553-
verbose=True,
554-
):
572+
year: Union[Literal["all"], int] = "all",
573+
output_tif: Optional[str] = None,
574+
melt_start_mmdd: Tuple[int, int] = (10, 1),
575+
melt_end_mmdd: Tuple[int, int] = (4, 30),
576+
gap_filled: bool = True,
577+
verbose: bool = True,
578+
) -> Optional[numpy.ndarray]:
555579
"""Create an integer tif file of that year's annual sum of melt-days, per pixel.
556580
557581
If gap_filled, create a floating-point tif file of the same.
@@ -568,9 +592,7 @@ def create_annual_melt_sum_tif(
568592
dt_list = list(datetimes_dict.keys())
569593

570594
if year == "all":
571-
years = numpy.unique([dt.year for dt in dt_list])
572-
years.sort()
573-
595+
years = sorted({dt.year for dt in dt_list})
574596
else:
575597
assert year == int(year)
576598
years = [year]
@@ -589,7 +611,7 @@ def create_annual_melt_sum_tif(
589611
day=melt_end_mmdd[1],
590612
)
591613

592-
dates_mask = numpy.array(
614+
dates_mask: numpy.ndarray = numpy.array(
593615
[((dt >= start_date) and (dt <= end_date)) for dt in dt_list],
594616
dtype=bool,
595617
)

antarctica_today/melt_array_picklefile.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import pickle
1616
import re
1717
from pathlib import Path
18-
from typing import Any
18+
from typing import Any, Dict, Tuple
1919

2020
import numpy
2121
from osgeo import gdal
@@ -323,15 +323,15 @@ def _filter_out_erroneous_swaths(model_array, datetimes_dict):
323323

324324

325325
def read_gap_filled_melt_picklefile(
326-
picklefile=gap_filled_melt_picklefile, verbose=True
327-
):
326+
picklefile: Path = gap_filled_melt_picklefile,
327+
verbose: bool = True,
328+
) -> Tuple[numpy.ndarray, Dict[datetime.datetime, int]]:
328329
"""Read the gap-filled picklefile, return to user."""
329330
if verbose:
330331
print("Reading", picklefile)
331332

332-
f = open(picklefile, "rb")
333-
array, dt_dict = pickle.load(f)
334-
f.close()
333+
with open(picklefile, "rb") as f:
334+
array, dt_dict = pickle.load(f)
335335

336336
return array, dt_dict
337337

0 commit comments

Comments
 (0)