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)
Esempio n. 2
0
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,
        )
Esempio n. 3
0
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)
Esempio n. 4
0
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)
Esempio n. 5
0
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)