11import datetime
22import os
33import pickle
4+ from pathlib import Path
5+ from typing import Dict , Literal , Optional , Tuple , Union
46
57import numpy
68import pandas
2628
2729
2830def 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
177179def 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
202204def 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
238241def 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
263266def 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
342347def 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
436452def 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
547571def 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 )
0 commit comments