def calculate_lake_effect_snowrate(u_10m=None, v_10m=None, tair_2m_degc=None, precip_m_per_s=None, snowfall_m_per_s=None, lake_fraction=None, lake_ice_fraction=None): # If snowfall is not passed => calculate it from the 2m air temperature and precip """ :param u_10m: :param v_10m: :param tair_2m_degc: :param precip_m_per_s: Total precipitation in M/s :param snowfall_m_per_s: """ if snowfall_m_per_s is None: assert tair_2m_degc is not None and precip_m_per_s is not None snowfall_m_per_s = base_utils.get_snow_fall_m_per_s( precip_m_per_s=precip_m_per_s, tair_deg_c=tair_2m_degc) if None in [u_10m, v_10m]: calculate_lake_effect_snowrate_based_on_snowfall( snowfall_m_per_s=snowfall_m_per_s) else: calculate_lake_effect_snowrate(u_10m=u_10m, v_10m=v_10m, snowfall_m_per_s=snowfall_m_per_s, lake_fraction=lake_fraction, lake_ice_fraction=lake_ice_fraction)
def calculate_lake_effect_snowrate( u_10m=None, v_10m=None, tair_2m_degc=None, precip_m_per_s=None, snowfall_m_per_s=None, lake_fraction=None, lake_ice_fraction=None, ): # If snowfall is not passed => calculate it from the 2m air temperature and precip """ :param u_10m: :param v_10m: :param tair_2m_degc: :param precip_m_per_s: Total precipitation in M/s :param snowfall_m_per_s: """ if snowfall_m_per_s is None: assert tair_2m_degc is not None and precip_m_per_s is not None snowfall_m_per_s = base_utils.get_snow_fall_m_per_s(precip_m_per_s=precip_m_per_s, tair_deg_c=tair_2m_degc) if None in [u_10m, v_10m]: calculate_lake_effect_snowrate_based_on_snowfall(snowfall_m_per_s=snowfall_m_per_s) else: calculate_lake_effect_snowrate( u_10m=u_10m, v_10m=v_10m, snowfall_m_per_s=snowfall_m_per_s, lake_fraction=lake_fraction, lake_ice_fraction=lake_ice_fraction, )
def calculate_enh_lakeffect_snowfall_for_a_datasource(data_mngr, label="", period=None, out_folder: Path = Path( ".")): months_of_interest = period.months_of_interest out_file = "{}_lkeff_snfl_{}-{}_m{:02d}-{:02d}.nc".format( label, period.start.year, period.end.year, months_of_interest[0], months_of_interest[-1], out_folder) lake_effect_zone_radius = DEFAULT_LAKE_EFFECT_ZONE_RADIUS_KM out_file = out_folder.joinpath(out_file) if out_file.exists(): print("{} already exists, won't redo!".format(out_file)) # plot_acc_snowfall_map(data_path=out_file, label=label, period=period, out_folder=out_folder, # months_of_interest=months_of_interest) return # for each period # 1. get daily snowfall # 2. get sum of daily snowfalls lkeff_snow_falls = [] lkeff_snow_fall_days = [] lkeff_snow_fall_eventcount = [] years_index = [] reg_of_interest = None lons = None lats = None lons_rad = None lats_rad = None ktree = None lake_mask = None near_lake_x_km_zone_mask = None ktree_for_nonlocal_snowfall_calculations = None secs_per_day = timedelta(days=1).total_seconds() for start in period.range("years"): end_date = start.add(months=len(months_of_interest)) end_date = min(period.end, end_date) end_date = end_date.subtract(seconds=1) sys.stderr.write( "start_date={}, end_date={}, period.end={}, months_of_interest={}\n" .format(start, end_date, period.end, months_of_interest)) p = Period(start, end_date) # build the name of the daily output file out_file_daily = "{}_lkeff_snfl_{}-{}_m{:02d}-{:02d}_daily.nc".format( label, p.start.year, p.end.year, months_of_interest[0], months_of_interest[-1], out_folder) out_file_daily = out_folder.joinpath(out_file_daily) print(f"Processing {p.start} ... {p.end} period") # try to read snowfall if not available, try to calculate from total precip try: snfl = data_mngr.read_data_for_period( p, default_varname_mappings.SNOWFALL_RATE) snfl = snfl.resample(t="1D", restore_coord_dims=True).mean(dim="t") rhosn = base_utils.get_snow_density_kg_per_m3( tair_deg_c=air_temp.values) # convert from water depth to snow depth snfl *= base_utils.WATER_DENSITY_KG_PER_M3 / rhosn except (IOError, KeyError, Exception): print(f"Could not find snowfall rate in {data_mngr.base_folder}") print( "Calculating from 2-m air temperature and total precipitation." ) try: air_temp = data_mngr.read_data_for_period( p, default_varname_mappings.T_AIR_2M) except IOError as e: print(e) continue # print("Calculating daily mean 2-m air temperature") air_temp = air_temp.resample(t="1D", restore_coord_dims=True).mean(dim="t") # use daily mean precip (to be consistent with the 2-meter air temperature) precip_m_s = data_mngr.read_data_for_period( p, default_varname_mappings.TOTAL_PREC) precip_m_s = precip_m_s.resample(t="1D", restore_coord_dims=True, keep_attrs=True).mean(dim="t") # Calculate snowfall from the total precipitation and 2-meter air temperature snfl = precip_m_s.copy() snfl.name = default_varname_mappings.SNOWFALL_RATE snfl.values = base_utils.get_snow_fall_m_per_s( precip_m_per_s=precip_m_s.values, tair_deg_c=air_temp.values) sys.stderr.write(f"{precip_m_s}\n") # print("===========air temp ranges=======") # print(air_temp.min(), " .. ", air_temp.max()) print("Snowfall values ranges: ") # print(snfl.min(), snfl.max(), common_params.lower_limit_of_daily_snowfall) # save snowfall total snfl_total = snfl.copy() snfl_total *= timedelta(days=1).total_seconds() snfl_total.attrs["units"] = "M/day" # set to 0 snowfall lower than 1 cm/day snfl.values[ snfl.values <= common_params.lower_limit_of_daily_snowfall] = 0 snfl *= timedelta(days=1).total_seconds() snfl.attrs["units"] = "M/day" years_index.append(start.year) if reg_of_interest is None: lons, lats = snfl.coords["lon"].values, snfl.coords["lat"].values # convert longitudes to the 0..360 range lons[lons < 0] += 360 reg_of_interest = common_params.great_lakes_limits.get_mask_for_coords( lons, lats) # temporary lake_mask = get_mask( lons, lats, shp_path=common_params.GL_COAST_SHP_PATH) > 0.1 # print("lake_mask shape", lake_mask.shape) # mask lake points reg_of_interest &= ~lake_mask # get the KDTree for interpolation purposes ktree = KDTree( np.array( list( zip(*lat_lon.lon_lat_to_cartesian( lon=lons.flatten(), lat=lats.flatten()))))) # define the ~200km near lake zone near_lake_x_km_zone_mask = get_zone_around_lakes_mask( lons=lons, lats=lats, lake_mask=lake_mask, ktree=ktree, dist_km=lake_effect_zone_radius) reg_of_interest &= near_lake_x_km_zone_mask lons_rad = np.radians(lons) lats_rad = np.radians(lats) # check the winds print("Reading the winds into memory") u_we = data_mngr.read_data_for_period(p, default_varname_mappings.U_WE) u_we = u_we.resample(t="1D", restore_coord_dims=True).mean(dim="t") v_sn = data_mngr.read_data_for_period(p, default_varname_mappings.V_SN) v_sn = v_sn.resample(t="1D", restore_coord_dims=True).mean(dim="t") print("Successfully imported wind components") assert len(v_sn.t) == len(np.unique(v_sn.t[:])) # Try to get the lake ice fractions lake_ice_fraction = None try: lake_ice_fraction = data_mngr.read_data_for_period( p, default_varname_mappings.LAKE_ICE_FRACTION) lake_ice_fraction = lake_ice_fraction.resample( t="1D", restore_coord_dims=True).mean( dim="t") # Calculate the daily means lake_ice_fraction = lake_ice_fraction.sel(t=v_sn.coords["t"], method="nearest") # update the time coordinates as well lake_ice_fraction.coords["t"] = v_sn.coords["t"][:] lake_ice_fraction = lake_ice_fraction.where( (lake_ice_fraction <= 1) & (lake_ice_fraction >= 0)) # at this point shapes of the arrays should be the same assert lake_ice_fraction.shape == u_we.shape print(lake_ice_fraction.coords["t"][0], lake_ice_fraction.coords["t"][-1]) except Exception as e: print(e) print( "WARNING: Could not find lake fraction in {}, " "diagnosing lake-effect snow without lake ice " "(NOTE: this could be OK for the months when there is no ice usually)" .format(data_mngr.base_folder)) lake_ice_fraction = snfl * 0 # raise e # take into account the wind direction wind_blows_from_lakes = winds.get_wind_blows_from_lakes_mask( lons, lats, u_we.values, v_sn.values, lake_mask, ktree=ktree, region_of_interest=reg_of_interest, dt_secs=secs_per_day, nneighbours=4, lake_ice_fraction=lake_ice_fraction, lons_rad=lons_rad, lats_rad=lats_rad) print("wind_blows_from_lakes.shape = ", wind_blows_from_lakes.shape) print("snfl.shape = ", snfl.shape) snfl = wind_blows_from_lakes * snfl # take into account nonlocal amplification due to lakes if ktree_for_nonlocal_snowfall_calculations is None: xs_nnlc, ys_nnlcl, zs_nnlcl = lat_lon.lon_lat_to_cartesian( lons.flatten(), lats.flatten()) ktree_for_nonlocal_snowfall_calculations = KDTree( list(zip(xs_nnlc, ys_nnlcl, zs_nnlcl))) snfl_nonlocal = get_nonlocal_mean_snowfall( lons=lons, lats=lats, region_of_interest=reg_of_interest, kdtree=ktree_for_nonlocal_snowfall_calculations, snowfall=snfl, lake_mask=lake_mask, outer_radius_km=500) snfl = (snfl > (common_params.snfl_local_amplification_m_per_s + snfl_nonlocal)).values * snfl # save daily data to file i_arr, j_arr = np.where(reg_of_interest) i_min, i_max = i_arr.min(), i_arr.max() j_min, j_max = j_arr.min(), j_arr.max() ds = snfl.loc[:, i_min:i_max + 1, j_min:j_max + 1].to_dataset(name="hles_snow") # import pickle # pickle.dump(lake_ice_fraction, open(str(out_file_daily) + ".bin", "wb")) ds["lake_ice_fraction"] = lake_ice_fraction.loc[:, i_min:i_max + 1, j_min:j_max + 1] ds["u_we"] = u_we.loc[:, i_min:i_max + 1, j_min:j_max + 1] ds["v_sn"] = v_sn.loc[:, i_min:i_max + 1, j_min:j_max + 1] ds["total_snowfall"] = snfl_total.loc[:, i_min:i_max + 1, j_min:j_max + 1] ds.to_netcdf(str(out_file_daily)) # count the number of hles events snfl_eventcount = snfl.copy() snfl_eventcount.values[snfl.values > 1e-5] = 1 snfl_eventcount.values[snfl.values <= 1e-5] = 0 snfl_eventcount = snfl_eventcount.diff("t", n=1) snfl_eventcount = (snfl_eventcount < 0).sum(dim="t") lkeff_snow_fall_eventcount.append(snfl_eventcount) # count the number of days with lake effect snowfall lkeff_snow_fall_days.append((snfl > 0).sum(dim="t")) # Get the accumulation of the lake effect snowfall snfl_acc = snfl.sum(dim="t") # takes into account the 100km zone near lakes # snfl_acc.values = np.ma.masked_where((~reg_of_interest) | (~near_lake_x_km_zone_mask), snfl_acc) snfl_acc.values = np.ma.masked_where((~reg_of_interest), snfl_acc) lkeff_snow_falls.append(snfl_acc) del snfl del snfl_nonlocal if len(years_index) == 0: print("Nothing to plot, exiting.") return # concatenate the yearly accumulated snowfall and save the result to a netcdf file # select the region of interest before saving calculated fields to the file years_index = DataArray(years_index, name="year", dims="year") i_arr, j_arr = np.where(reg_of_interest) i_min, i_max = i_arr.min(), i_arr.max() j_min, j_max = j_arr.min(), j_arr.max() snfl_yearly = xarray.concat([ arr.loc[i_min:i_max + 1, j_min:j_max + 1] for arr in lkeff_snow_falls ], dim=years_index) snfl_yearly.attrs["units"] = "m" snfl_days_yearly = xarray.concat([ arr.loc[i_min:i_max + 1, j_min:j_max + 1] for arr in lkeff_snow_fall_days ], dim=years_index) snfl_days_yearly.attrs["units"] = "days" snfl_eventcounts_yearly = xarray.concat([ arr.loc[i_min:i_max + 1, j_min:j_max + 1] for arr in lkeff_snow_fall_eventcount ], dim=years_index) snfl_eventcounts_yearly.attrs["units"] = "number of events" ds = snfl_yearly.to_dataset() assert isinstance(ds, xarray.Dataset) ds["lkeff_snowfall_days"] = (("year", "x", "y"), snfl_days_yearly) ds["lkeff_snowfall_eventcount"] = (("year", "x", "y"), snfl_eventcounts_yearly) ds.to_netcdf(str(out_file)) ds.close() # do the plotting plot_acc_snowfall_map(data_path=out_file, label=label, period=period, out_folder=out_folder, months_of_interest=period.months_of_interest)
def calculate_enh_lakeffect_snowfall_for_a_datasource(data_mngr, label="", period=None, out_folder: Path = Path(".")): months_of_interest = period.months_of_interest out_file = "{}_lkeff_snfl_{}-{}_m{:02d}-{:02d}.nc".format(label, period.start.year, period.end.year, months_of_interest[0], months_of_interest[-1], out_folder) lake_effect_zone_radius = DEFAULT_LAKE_EFFECT_ZONE_RADIUS_KM out_file = out_folder.joinpath(out_file) if out_file.exists(): print("{} already exists, won't redo!".format(out_file)) # plot_acc_snowfall_map(data_path=out_file, label=label, period=period, out_folder=out_folder, # months_of_interest=months_of_interest) return # for each period # 1. get daily snowfall # 2. get sum of daily snowfalls lkeff_snow_falls = [] lkeff_snow_fall_days = [] lkeff_snow_fall_eventcount = [] years_index = [] reg_of_interest = None lons = None lats = None lons_rad = None lats_rad = None ktree = None lake_mask = None near_lake_x_km_zone_mask = None ktree_for_nonlocal_snowfall_calculations = None secs_per_day = timedelta(days=1).total_seconds() for start in period.range("years"): end_date = start.add(months=len(months_of_interest)) end_date = min(period.end, end_date) end_date = end_date.subtract(seconds=1) print("start_date={}, end_date={}, period.end={}, months_of_interest={}".format(start, end_date, period.end, months_of_interest)) p = Period(start, end_date) # build the name of the daily output file out_file_daily = "{}_lkeff_snfl_{}-{}_m{:02d}-{:02d}_daily.nc".format(label, p.start.year, p.end.year, months_of_interest[0], months_of_interest[-1], out_folder) out_file_daily = out_folder.joinpath(out_file_daily) print(f"Processing {p.start} ... {p.end} period") # try to read snowfall if not available, try to calculate from total precip try: snfl = data_mngr.read_data_for_period(p, default_varname_mappings.SNOWFALL_RATE) snfl = snfl.resample("1D", dim="t", how="mean") rhosn = base_utils.get_snow_density_kg_per_m3(tair_deg_c=air_temp.values) # convert from water depth to snow depth snfl *= base_utils.WATER_DENSITY_KG_PER_M3 / rhosn except (IOError, KeyError, Exception): print(f"Could not find snowfall rate in {data_mngr.base_folder}") print("Calculating from 2-m air temperature and total precipitation.") try: air_temp = data_mngr.read_data_for_period(p, default_varname_mappings.T_AIR_2M) except IOError as e: print(e) continue # print("Calculating daily mean 2-m air temperature") air_temp = air_temp.resample("1D", dim="t", how="mean") # use daily mean precip (to be consistent with the 2-meter air temperature) precip_m_s = data_mngr.read_data_for_period(p, default_varname_mappings.TOTAL_PREC) precip_m_s = precip_m_s.resample("1D", dim="t", how="mean") # Calculate snowfall from the total precipitation and 2-meter air temperature snfl = precip_m_s.copy() snfl.name = default_varname_mappings.SNOWFALL_RATE snfl.values = base_utils.get_snow_fall_m_per_s(precip_m_per_s=precip_m_s.values, tair_deg_c=air_temp.values) print("===========air temp ranges=======") # print(air_temp.min(), " .. ", air_temp.max()) print("Snowfall values ranges: ") # print(snfl.min(), snfl.max(), common_params.lower_limit_of_daily_snowfall) # save snowfall total snfl_total = snfl.copy() snfl_total *= timedelta(days=1).total_seconds() snfl_total.attrs["units"] = "M/day" # set to 0 snowfall lower than 1 cm/day snfl.values[snfl.values <= common_params.lower_limit_of_daily_snowfall] = 0 snfl *= timedelta(days=1).total_seconds() snfl.attrs["units"] = "M/day" years_index.append(start.year) if reg_of_interest is None: lons, lats = snfl.coords["lon"].values, snfl.coords["lat"].values # convert longitudes to the 0..360 range lons[lons < 0] += 360 reg_of_interest = common_params.great_lakes_limits.get_mask_for_coords(lons, lats) # temporary lake_mask = get_mask(lons, lats, shp_path=common_params.GL_COAST_SHP_PATH) > 0.1 # print("lake_mask shape", lake_mask.shape) # mask lake points reg_of_interest &= ~lake_mask # get the KDTree for interpolation purposes ktree = KDTree( np.array(list(zip(*lat_lon.lon_lat_to_cartesian(lon=lons.flatten(), lat=lats.flatten())))) ) # define the ~200km near lake zone near_lake_x_km_zone_mask = get_zone_around_lakes_mask(lons=lons, lats=lats, lake_mask=lake_mask, ktree=ktree, dist_km=lake_effect_zone_radius) reg_of_interest &= near_lake_x_km_zone_mask lons_rad = np.radians(lons) lats_rad = np.radians(lats) # check the winds print("Reading the winds into memory") u_we = data_mngr.read_data_for_period(p, default_varname_mappings.U_WE) u_we = u_we.resample("1D", dim="t", how="mean") v_sn = data_mngr.read_data_for_period(p, default_varname_mappings.V_SN) v_sn = v_sn.resample("1D", dim="t", how="mean") print("Successfully imported wind components") assert len(v_sn.t) == len(np.unique(v_sn.t[:])) # Try to get the lake ice fractions lake_ice_fraction = None try: lake_ice_fraction = data_mngr.read_data_for_period(p, default_varname_mappings.LAKE_ICE_FRACTION) lake_ice_fraction = lake_ice_fraction.resample("1D", dim="t", how="mean") # Calculate the daily means lake_ice_fraction = lake_ice_fraction.sel(t=v_sn.coords["t"], method="nearest") # update the time coordinates as well lake_ice_fraction.coords["t"] = v_sn.coords["t"][:] lake_ice_fraction = lake_ice_fraction.where((lake_ice_fraction <= 1) & (lake_ice_fraction >= 0)) # at this point shapes of the arrays should be the same assert lake_ice_fraction.shape == u_we.shape print(lake_ice_fraction.coords["t"][0], lake_ice_fraction.coords["t"][-1]) except Exception as e: print(e) print("WARNING: Could not find lake fraction in {}, " "diagnosing lake-effect snow without lake ice " "(NOTE: this could be OK for the months when there is no ice usually)".format(data_mngr.base_folder)) lake_ice_fraction = snfl * 0 # raise e # take into account the wind direction wind_blows_from_lakes = winds.get_wind_blows_from_lakes_mask(lons, lats, u_we.values, v_sn.values, lake_mask, ktree=ktree, region_of_interest=reg_of_interest, dt_secs=secs_per_day, nneighbours=4, lake_ice_fraction=lake_ice_fraction, lons_rad=lons_rad, lats_rad=lats_rad) print("wind_blows_from_lakes.shape = ", wind_blows_from_lakes.shape) print("snfl.shape = ", snfl.shape) snfl = wind_blows_from_lakes * snfl # take into account nonlocal amplification due to lakes if ktree_for_nonlocal_snowfall_calculations is None: xs_nnlc, ys_nnlcl, zs_nnlcl = lat_lon.lon_lat_to_cartesian(lons.flatten(), lats.flatten()) ktree_for_nonlocal_snowfall_calculations = KDTree(list(zip(xs_nnlc, ys_nnlcl, zs_nnlcl))) snfl_nonlocal = get_nonlocal_mean_snowfall(lons=lons, lats=lats, region_of_interest=reg_of_interest, kdtree=ktree_for_nonlocal_snowfall_calculations, snowfall=snfl, lake_mask=lake_mask, outer_radius_km=500) snfl = (snfl > (common_params.snfl_local_amplification_m_per_s + snfl_nonlocal)).values * snfl # save daily data to file i_arr, j_arr = np.where(reg_of_interest) i_min, i_max = i_arr.min(), i_arr.max() j_min, j_max = j_arr.min(), j_arr.max() ds = snfl.loc[:, i_min:i_max + 1, j_min:j_max + 1].to_dataset(name="hles_snow") # import pickle # pickle.dump(lake_ice_fraction, open(str(out_file_daily) + ".bin", "wb")) ds["lake_ice_fraction"] = lake_ice_fraction.loc[:, i_min:i_max + 1, j_min:j_max + 1] ds["u_we"] = u_we.loc[:, i_min:i_max + 1, j_min:j_max + 1] ds["v_sn"] = v_sn.loc[:, i_min:i_max + 1, j_min:j_max + 1] ds["total_snowfall"] = snfl_total.loc[:, i_min:i_max + 1, j_min:j_max + 1] ds.to_netcdf(str(out_file_daily)) # count the number of hles events snfl_eventcount = snfl.copy() snfl_eventcount.values[snfl.values > 1e-5] = 1 snfl_eventcount.values[snfl.values <= 1e-5] = 0 snfl_eventcount = snfl_eventcount.diff("t", n=1) snfl_eventcount = (snfl_eventcount < 0).sum(dim="t") lkeff_snow_fall_eventcount.append(snfl_eventcount) # count the number of days with lake effect snowfall lkeff_snow_fall_days.append((snfl > 0).sum(dim="t")) # Get the accumulation of the lake effect snowfall snfl_acc = snfl.sum(dim="t") # takes into account the 100km zone near lakes # snfl_acc.values = np.ma.masked_where((~reg_of_interest) | (~near_lake_x_km_zone_mask), snfl_acc) snfl_acc.values = np.ma.masked_where((~reg_of_interest), snfl_acc) lkeff_snow_falls.append(snfl_acc) del snfl del snfl_nonlocal if len(years_index) == 0: print("Nothing to plot, exiting.") return # concatenate the yearly accumulated snowfall and save the result to a netcdf file # select the region of interest before saving calculated fields to the file years_index = DataArray(years_index, name="year", dims="year") i_arr, j_arr = np.where(reg_of_interest) i_min, i_max = i_arr.min(), i_arr.max() j_min, j_max = j_arr.min(), j_arr.max() snfl_yearly = xarray.concat([arr.loc[i_min: i_max + 1, j_min: j_max + 1] for arr in lkeff_snow_falls], dim=years_index) snfl_yearly.attrs["units"] = "m" snfl_days_yearly = xarray.concat([arr.loc[i_min: i_max + 1, j_min: j_max + 1] for arr in lkeff_snow_fall_days], dim=years_index) snfl_days_yearly.attrs["units"] = "days" snfl_eventcounts_yearly = xarray.concat( [arr.loc[i_min: i_max + 1, j_min: j_max + 1] for arr in lkeff_snow_fall_eventcount], dim=years_index) snfl_eventcounts_yearly.attrs["units"] = "number of events" ds = snfl_yearly.to_dataset() assert isinstance(ds, xarray.Dataset) ds["lkeff_snowfall_days"] = (("year", "x", "y"), snfl_days_yearly) ds["lkeff_snowfall_eventcount"] = (("year", "x", "y"), snfl_eventcounts_yearly) ds.to_netcdf(str(out_file)) ds.close() # do the plotting plot_acc_snowfall_map(data_path=out_file, label=label, period=period, out_folder=out_folder, months_of_interest=period.months_of_interest)
def calculate_enh_lakeffect_snowfall_for_a_datasource(data_mngr, label="", period=None, out_folder="."): months_of_interest = period.months_of_interest if not isinstance(out_folder, Path): out_folder_p = Path(out_folder) else: out_folder_p = out_folder # Try to create the output folder if it does not exist if not out_folder_p.exists(): out_folder_p.mkdir() out_file = "{}_lkeff_snfl_{}-{}_m{}-{}.nc".format( label, period.start.year, period.end.year, months_of_interest[0], months_of_interest[-1], out_folder ) out_file = str(out_folder_p.joinpath(out_file)) # for each period # 1. get daily snowfall # 2. get sum of daily snowfalls lkeff_snow_falls = [] lkeff_snow_fall_days = [] years_index = [] reg_of_interest = None lons = None lats = None ktree = None lake_mask = None near_lake_100km_zone_mask = None secs_per_day = timedelta(days=1).total_seconds() for start in period.range("years"): p = Period(start, start.add(months=len(months_of_interest)).subtract(seconds=1)) print("Processing {} ... {} period".format(p.start, p.end)) try: air_temp = data_mngr.read_data_for_period(p, default_varname_mappings.T_AIR_2M) except IOError as e: print(e) continue day_dates = [datetime(d.year, d.month, d.day) for d in pd.to_datetime(air_temp.coords["t"].values)] day_dates = DataArray(day_dates, name="time", dims="t") air_temp = air_temp.groupby(day_dates).mean(dim="t") # try to read snowfall if not available, try to calculate from total precip try: snfl = data_mngr.read_data_for_period(p, default_varname_mappings.SNOWFALL_RATE) snfl = snfl.groupby(day_dates).mean(dim="t") rhosn = base_utils.get_snow_density_kg_per_m3(tair_deg_c=air_temp.values) # convert from water depth to snow depth snfl *= base_utils.WATER_DENSITY_KG_PER_M3 / rhosn except (IOError, KeyError): print("Could not find snowfall rate in {}".format(data_mngr.base_folder)) print("Calculating from 2-m air temperature and total precipitation.") # use daily mean precip (to be consistent with the 2-meter air temperature) precip_m_s = data_mngr.read_data_for_period(p, default_varname_mappings.TOTAL_PREC) precip_m_s = precip_m_s.groupby(day_dates).mean(dim="t") # Calculate snowfall from the total precipitation and 2-meter air temperature snfl = precip_m_s.copy() snfl.name = default_varname_mappings.SNOWFALL_RATE snfl.values = base_utils.get_snow_fall_m_per_s(precip_m_per_s=precip_m_s.values, tair_deg_c=air_temp.values) print("===========air temp ranges=======") print(air_temp.min(), " .. ", air_temp.max()) print("Snowfall values ranges: ") print(snfl.min(), snfl.max(), common_params.lower_limit_of_daily_snowfall) # set to 0 snowfall lower than 1 cm/day snfl.values[snfl.values <= common_params.lower_limit_of_daily_snowfall] = 0 snfl *= timedelta(days=1).total_seconds() assert isinstance(snfl, DataArray) years_index.append(start.year) if reg_of_interest is None: lons, lats = snfl.coords["lon"].values, snfl.coords["lat"].values reg_of_interest = common_params.great_lakes_limits.get_mask_for_coords(lons, lats) # temporary lake_mask = get_mask(lons, lats, shp_path=common_params.GL_COAST_SHP_PATH) > 0.1 print("lake_mask shape", lake_mask.shape) # mask lake points reg_of_interest &= ~lake_mask # get the KDTree for interpolation purposes ktree = KDTree(data=list(zip(*lat_lon.lon_lat_to_cartesian(lon=lons.flatten(), lat=lats.flatten())))) # define the 100km near lake zone # near_lake_100km_zone_mask = get_zone_around_lakes_mask(lons=lons, lats=lats, lake_mask=lake_mask, # ktree=ktree, dist_km=200) # check the winds print("Reading the winds into memory") u_we = data_mngr.read_data_for_period(p, default_varname_mappings.U_WE) u_we = u_we.groupby(day_dates).mean(dim="t") v_sn = data_mngr.read_data_for_period(p, default_varname_mappings.V_SN) v_sn = v_sn.groupby(day_dates).mean(dim="t") print("Successfully imported wind components") wind_blows_from_lakes = winds.get_wind_blows_from_lakes_mask( lons, lats, u_we.values, v_sn.values, lake_mask, ktree=ktree, region_of_interest=reg_of_interest, dt_secs=secs_per_day, nneighbours=4, ) snfl = wind_blows_from_lakes * snfl # count the number of days with lake effect snowfall lkeff_snow_fall_days.append((snfl > 0).sum(dim="time")) # Get the accumulation of the lake effect snowfall snfl_acc = snfl.sum(dim="time") # takes into account the 100km zone near lakes # snfl_acc.values = np.ma.masked_where((~reg_of_interest) | (~near_lake_100km_zone_mask), snfl_acc) snfl_acc.values = np.ma.masked_where((~reg_of_interest), snfl_acc) lkeff_snow_falls.append(snfl_acc) if len(years_index) == 0: print("Nothing to plot, exiting.") return # concatenate the yearly accumulated snowfall and save the result to a netcdf file # select the region of interest before saving calculated fields to the file years_index = DataArray(years_index, name="year", dims="year") i_arr, j_arr = np.where(reg_of_interest) i_min, i_max = i_arr.min(), i_arr.max() j_min, j_max = j_arr.min(), j_arr.max() snfl_yearly = xarray.concat( [arr.loc[i_min : i_max + 1, j_min : j_max + 1] for arr in lkeff_snow_falls], dim=years_index ) snfl_yearly.attrs["units"] = "m" snfl_days_yearly = xarray.concat( [arr.loc[i_min : i_max + 1, j_min : j_max + 1] for arr in lkeff_snow_fall_days], dim=years_index ) snfl_days_yearly.attrs["units"] = "days" ds = snfl_yearly.to_dataset() assert isinstance(ds, xarray.Dataset) ds["lkeff_snowfall_days"] = (("year", "x", "y"), snfl_days_yearly) ds.to_netcdf(out_file) # Plot snowfall maps for each year clevs_total_snowfall = [0, 10, 50, 90, 130, 170, 210, 250, 400, 500] clevs_lkeff_snowfall = [0, 1, 2, 10, 15, 20, 40, 80, 120, 160] clevs = clevs_lkeff_snowfall b = Basemap( lon_0=180, llcrnrlon=common_params.great_lakes_limits.lon_min, llcrnrlat=common_params.great_lakes_limits.lat_min, urcrnrlon=common_params.great_lakes_limits.lon_max, urcrnrlat=common_params.great_lakes_limits.lat_max, resolution="i", ) xx, yy = b(lons, lats) print("Basemap corners: ", lons[i_min, j_min] - 360, lons[i_max, j_max] - 360) plot_utils.apply_plot_params(font_size=10) fig = plt.figure() ncols = 3 nrows = len(years_index) // ncols + 1 gs = GridSpec(ncols=ncols, nrows=nrows) # bn = BoundaryNorm(clevs, len(clevs) - 1) # cmap = cm.get_cmap("nipy_spectral") cmap, bn = colors.from_levels_and_colors( clevs, ["indigo", "blue", "dodgerblue", "aqua", "lime", "yellow", "gold", "orange", "red"] ) area_avg_lkeff_snowfall = [] for i, y in enumerate(years_index.values): col = i % ncols row = i // ncols ax = fig.add_subplot(gs[row, col]) to_plot = np.ma.masked_where(~reg_of_interest, lkeff_snow_falls[i]) print(xx.shape, to_plot.shape) to_plot *= 100 # convert to cm im = b.contourf(xx, yy, to_plot, norm=bn, cmap=cmap, levels=clevs) area_avg_lkeff_snowfall.append(to_plot[(~to_plot.mask) & (to_plot > 0)].mean()) cb = b.colorbar(im, ax=ax) cb.ax.set_title("cm") b.drawcoastlines() b.drawparallels(np.arange(-90, 90, 10), labels=[1, 0, 0, 1]) b.drawmeridians(np.arange(-180, 180, 10), labels=[1, 0, 0, 1]) ax.set_title("{}".format(y)) fig.tight_layout() img_file = "{}_acc_lakeff_snow_{}-{}.png".format(label, period.start.year, period.end.year - 1) img_file = str(out_folder_p.joinpath(img_file)) plt.savefig(img_file, bbox_inches="tight") # plt.show() plt.close(fig) # plot area-averaged lake-effect snowfall fig = plt.figure() ax = plt.gca() ax.plot(years_index.values.astype(int), area_avg_lkeff_snowfall, "r", lw=2) ax.set_title("Area averaged annual lake-effect snowfall") sf = ScalarFormatter(useOffset=False) ax.xaxis.set_major_formatter(sf) ax.grid() fig.tight_layout() img_file = "{}_acc_lakeff_snow_area_avg_{}-{}.png".format(label, period.start.year, period.end.year - 1) img_file = str(out_folder_p.joinpath(img_file)) plt.savefig(img_file, bbox_inches="tight") plt.close(fig)