Пример #1
0
def _remove_ensemble_mean_trend(da, dim="init_date", ensemble_dim="ensemble"):
    """Remove ensemble mean trend along given dimension.

    Parameters
    ----------
    da : xarray DataArray
        Input data array
    dim : str, default init_date
        Dimension over which to calculate and remove trend
    init_dim: str, default 'init_date'
        Name of the initial date dimension to create in the output
    ensemble_dim : str, default 'ensemble'
        Name of the ensemble member dimension in da

    Returns
    -------
    da_detrended : xarray DataArray
        Detrended data array
    """

    ensmean_trend = da.mean(ensemble_dim).polyfit(dim=dim, deg=1)
    ensmean_trend_line = xr.polyval(da[dim], ensmean_trend["polyfit_coefficients"])
    ensmean_trend_line_anomaly = ensmean_trend_line - ensmean_trend_line.isel({dim: 0})
    da_detrended = da - ensmean_trend_line_anomaly

    return da_detrended
Пример #2
0
def test_polyval(use_dask, use_datetime):
    if use_dask and not has_dask:
        pytest.skip("requires dask")

    if use_datetime:
        xcoord = xr.DataArray(pd.date_range("2000-01-01", freq="D",
                                            periods=10),
                              dims=("x", ),
                              name="x")
        x = xr.core.missing.get_clean_interp_index(xcoord, "x")
    else:
        xcoord = x = np.arange(10)

    da = xr.DataArray(
        np.stack((1.0 + x + 2.0 * x**2, 1.0 + 2.0 * x + 3.0 * x**2)),
        dims=("d", "x"),
        coords={
            "x": xcoord,
            "d": [0, 1]
        },
    )
    coeffs = xr.DataArray(
        [[2, 1, 1], [3, 2, 1]],
        dims=("d", "degree"),
        coords={
            "d": [0, 1],
            "degree": [2, 1, 0]
        },
    )
    if use_dask:
        coeffs = coeffs.chunk({"d": 2})

    da_pv = xr.polyval(da.x, coeffs)

    xr.testing.assert_allclose(da, da_pv.T)
Пример #3
0
def odyn_loc(SCE, MOD, DIR_O, DIR_OG, LOC, \
             ref_steric, ye, N, ys, Gam, NormD, LowPass):
    '''Compute the ocean dynamics and thermal expansion contribution to local sea
    level using KNMI14 files.'''

    nb_MOD = len(MOD)
    nb_y2 = ye - ys + 1
    lat_N, lat_S, lon_W, lon_E = LOC

    #For KNMI files the SSH has a different name for each scenario
    SSH_VAR_dic = {'rcp45': 'ZOSH45', 'rcp85': 'ZOS85'}
    SSH_VAR = SSH_VAR_dic[SCE]

    # Read sterodynamics and global steric contributions
    for m in range(nb_MOD):
        fi = xr.open_dataset(f'{DIR_O}{MOD[m]}_{SCE}.nc', use_cftime=True)
        fig = xr.open_dataset(f'{DIR_OG}{MOD[m]}_{SCE}.nc', use_cftime=True)
        fi = fi.assign_coords(TIME=fi['TIME.year'])
        fig = fig.assign_coords(time=fig['time.year']).squeeze()
        sd_da = fi[SSH_VAR].assign_coords(model=MOD[m])
        st_da = fig[SSH_VAR].assign_coords(model=MOD[m])

        if m == 0:
            full_sd_da = sd_da
            full_st_da = st_da
        else:
            full_sd_da = xr.concat([full_sd_da, sd_da], dim='model')
            full_st_da = xr.concat([full_st_da, st_da], dim='model')

    full_sd_da = full_sd_da.rename({'TIME': 'time'})
    full_sd_da = full_sd_da.sel(time=slice(ref_steric[0], ye),
                                latitude=slice(lat_S, lat_N),
                                longitude=slice(lon_W, lon_E))

    MAT = full_sd_da.mean(dim=['latitude', 'longitude'])
    MAT_G = full_st_da.sel(time=slice(ref_steric[0], ye))

    MAT = MAT - MAT.sel(time=slice(ref_steric[0], ref_steric[1])).mean(
        dim='time')

    if LowPass:
        fit_coeff = MAT.polyfit('time', 3)
        MAT = xr.polyval(coord=MAT.time, coeffs=fit_coeff.polyfit_coefficients)

    MAT_G = MAT_G - MAT_G.sel(time=slice(ref_steric[0], ref_steric[1])).mean(
        dim='time')
    MAT_A = MAT - MAT_G

    list_da = [MAT, MAT_G, MAT_A]

    X_O_out = np.zeros([3, N, nb_y2])

    for idx, da in enumerate(list_da):
        # Select years after the reference period and convert from m to cm
        da = da.sel(time=slice(ys, None)) * 100
        X_O_out[idx, :, :] = misc.normal_distrib(da, Gam, NormD)

    return X_O_out
Пример #4
0
    def _get_trend(self, grpd, dim="time"):
        # Estimate trend over da
        trend = xr.polyval(coord=grpd[dim], coeffs=grpd.polyfit_coefficients)

        if self.preserve_mean:
            trend = apply_correction(trend,
                                     invert(trend.mean(dim=dim), self.kind),
                                     self.kind)

        return trend
Пример #5
0
    def _get_trend(self, da):
        # Estimate trend over da
        trend = xr.polyval(coord=da[self._fitted_dim],
                           coeffs=self._fitds.polyfit_coefficients)

        if self.preserve_mean:
            trend = apply_correction(
                trend, invert(trend.mean(dim=self._fitted_dim), self.kind),
                self.kind)

        return trend
Пример #6
0
def rm_poly(ds: Union[xr.Dataset, xr.DataArray],
            dim: str = "time",
            deg: int = 2,
            **kwargs: Any) -> Union[xr.Dataset, xr.DataArray]:
    """Remove degree polynomial of degree ``deg`` along dimension ``dim``."""
    coefficients = ds.polyfit(dim, deg=deg, **kwargs)
    coord = ds[dim]
    fits = []
    if isinstance(ds, xr.Dataset):
        for v in coefficients:
            name = v.replace("_polyfit_coefficients", "")
            fit = xr.polyval(coord, coefficients[v]).rename(name)
            fits.append(fit)
        fits = xr.merge(fits)
    elif isinstance(ds, xr.DataArray):
        name = ds.name
        v = list(coefficients.data_vars)[0]
        fits = xr.polyval(coord, coefficients[v]).rename(name)
    with xr.set_options(keep_attrs=True):
        ds_rm_poly = ds - fits
    return ds_rm_poly
Пример #7
0
def _polydetrend_get_trend(da, *, dim, degree, preserve_mean, kind):
    """Polydetrend, atomic func on 1 group."""
    if len(dim) > 1:
        da = da.mean(dim[1:])
    dim = dim[0]
    pfc = da.polyfit(dim=dim, deg=degree)
    trend = xr.polyval(coord=da[dim], coeffs=pfc.polyfit_coefficients)

    if preserve_mean:
        trend = apply_correction(trend, invert(trend.mean(dim=dim), kind),
                                 kind)
    out = trend.rename("trend").to_dataset()
    return out
Пример #8
0
    def detrend(self, dim=['time'], degree=1):
        """Remove a polynomial trend from each dimension, one at a time.

        """
        if type(dim) is not type([]): dim = [dim]
        assert set(dim).issubset(set(self.ds[self.varname].dims))
        for d in dim:
            p = self.ds[self.varname].polyfit(dim=d, deg=degree)
            fit = xr.polyval(self.ds[self.varname][d], p.polyfit_coefficients)
            self.ds[self.varname] -= fit
        if 'ilamb' not in self.ds[self.varname].attrs:
            self.ds[self.varname].attrs['ilamb'] = ''
        dim = ["'%s'" % d for d in dim]
        self.ds[
            self.varname].attrs['ilamb'] += "detrend(dim=[%s],degree=%d); " % (
                ",".join(dim), degree)
        return self
Пример #9
0
def detrend_array(arr, dim="time", deg=1):
    """Detrend an Xarray DataArray

    Parameters
    ----------
    arr : xarray.DataArray
        Input data array
    dim : str, optional
        Name of time dimension, by default "time"
    deg : int, optional
        Number of polynomial coefficients for fit, by default 1 (linear)

    Returns
    -------
    xarray.DataArray
        Detrended data array
    """
    coeffs = arr.polyfit(dim=dim, deg=deg)
    fitted = xr.polyval(arr[dim], coeffs.polyfit_coefficients)
    return arr - fitted
Пример #10
0
# Read ocean dynamics and extrapolate if necessary
DIR_CDE = '/Users/dewilebars/Projects/Project_ProbSLR/CMIP_SeaLevel/outputs/'
fcde = misc.read_zos_ds(data_dir, SCE)

if cde_opt == 'mean':
    CDE = fcde.CorrectedReggrided_zos.mean(dim='model')
else:
    CDE = fcde.CorrectedReggrided_zos.sel(model=cde_opt)

if YEARS[-1] > 2100:
    new_time = xr.DataArray(np.array(YEARS + 0.5),
                            dims='time',
                            coords=[np.array(YEARS + 0.5)],
                            name='time')
    fit_coeff = CDE.polyfit('time', 2)
    CDE = xr.polyval(coord=new_time, coeffs=fit_coeff.polyfit_coefficients)

# Read global sea level projection
#fcomp = xr.open_dataset(f'../outputs/ref_proj/SeaLevelPerc_{namelist_name}_{SCE}.nc')
fcomp = xr.open_dataset(f'../outputs/SeaLevelPerc_{namelist_name}_{SCE}.nc')

# Read fingerprints
f_gic = xr.open_dataset(f'{DIR_F}Relative_GLACIERS.nc', decode_times=False)
f_gic = f_gic.assign_coords({'time': np.arange(1986, 2101)})
f_gic = f_gic.rename({'longitude': 'lon', 'latitude': 'lat'})
f_gic = misc.rotate_longitude(f_gic, 'lon')

f_ic = xr.open_dataset(
    f'{DIR_F}Relative_icesheets.nc') / 100  # Convert from % to fraction
f_ic = f_ic.rename({'longitude': 'lon', 'latitude': 'lat'})
f_ic = misc.rotate_longitude(f_ic, 'lon')
Пример #11
0
 def peakmem_polyval(self, nx, ndeg):
     x = self.xs[nx]
     c = self.coeffs[ndeg]
     xr.polyval(x, c).compute()
Пример #12
0
def tglob_cmip(data_dir, temp_opt, sce, start_date, ye, LowPass=False):
    '''Read the text files of monthly temperature for each CMIP5 model and store
    yearly averged values in an xarray DataArray.
    Output data is in degree Kelvin'''

    nb_y = ye - start_date + 1

    if temp_opt in ['CMIP5', 'AR5']:
        temp_data_dir = f'{data_dir}Data_AR5/Tglobal/'
        if temp_opt == 'CMIP5':
            path = f'{temp_data_dir}global_tas_Amon_*_{sce}_r1i1p1.dat'
            files = glob.glob(path)
        elif temp_opt == 'AR5':
            files = temp_path_AR5(temp_data_dir, sce)

        model_names = [f[86:-17] for f in files]
        df = pd.DataFrame({
            'clean_model_names': model_names,
            'file_names': files
        })

    elif temp_opt == 'CMIP6':
        temp_data_dir = f'{data_dir}Data_cmip6/tas_global_averaged_climate_explorer/'
        df = select_tglob_cmip6_files(temp_data_dir, sce)

    else:
        print(f'ERROR: Value of TEMP: {mip} not recognized')

    col_names = [
        'Year', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
        'Oct', 'Nov', 'Dec'
    ]

    for i in range(len(df)):
        TEMP = pd.read_csv(df.file_names.iloc[i],
                           comment='#',
                           delim_whitespace=True,
                           names=col_names)
        TEMP = TEMP.set_index('Year')
        TGLOBi = xr.DataArray(TEMP.mean(axis=1))
        mod = df.clean_model_names.iloc[i]
        TGLOBi = TGLOBi.expand_dims({'model': [mod]})

        if i == 0:
            TGLOB = TGLOBi
        else:
            TGLOB = xr.concat([TGLOB, TGLOBi], dim='model')

    TGLOB = TGLOB.rename({'Year': 'time'})
    TGLOB = TGLOB.sel(time=slice(start_date, ye))

    if LowPass:
        new_time = xr.DataArray(np.arange(start_date, ye + 1),
                                dims='time',
                                coords=[np.arange(start_date, ye + 1)],
                                name='time')
        fit_coeff = TGLOB.polyfit('time', 2)
        TGLOB = xr.polyval(coord=new_time,
                           coeffs=fit_coeff.polyfit_coefficients)

    TGLOB.name = 'GSAT'

    return TGLOB
Пример #13
0
def snowfall_approximation(
    pr: xr.DataArray,
    tas: xr.DataArray,
    thresh: str = "0 degC",
    method: str = "binary",
) -> xr.DataArray:
    """Snowfall approximation from total precipitation and temperature.

    Solid precipitation estimated from precipitation and temperature according to a given method.

    Parameters
    ----------
    pr : xarray.DataArray
      Mean daily precipitation flux.
    tas : xarray.DataArray, optional
      Mean, maximum, or minimum daily temperature.
    thresh : str,
      Threshold temperature, used by method "binary".
    method : {"binary", "brown", "auer"}
      Which method to use when approximating snowfall from total precipitation. See notes.

    Returns
    -------
    xarray.DataArray, [same units as pr]
      Solid precipitation flux.

    Notes
    -----
    The following methods are available to approximate snowfall and are drawn from the
    Canadian Land Surface Scheme (CLASS, [Verseghy09]_).

    - ``'binary'`` : When the temperature is under the freezing threshold, precipitation
      is assumed to be solid. The method is agnostic to the type of temperature used
      (mean, maximum or minimum).
    - ``'brown'`` : The phase between the freezing threshold goes from solid to liquid linearly
      over a range of 2°C over the freezing point.
    - ``'auer'`` : The phase between the freezing threshold goes from solid to liquid as a degree six
      polynomial over a range of 6°C over the freezing point.

    References
    ----------
    .. [Verseghy09] Diana Verseghy (2009), CLASS – The Canadian Land Surface Scheme (Version 3.4), Technical
       Documentation (Version 1.1), Environment Canada, Climate Research Division, Science and Technology Branch.

    https://gitlab.com/cccma/classic/-/blob/master/src/atmosphericVarsCalc.f90
    """

    if method == "binary":
        thresh = convert_units_to(thresh, tas)
        prsn = pr.where(tas <= thresh, 0)

    elif method == "brown":
        # Freezing point + 2C in the native units
        upper = convert_units_to(convert_units_to(thresh, "degC") + 2, tas)
        thresh = convert_units_to(thresh, tas)

        # Interpolate fraction over temperature (in units of tas)
        t = xr.DataArray([-np.inf, thresh, upper, np.inf],
                         dims=("tas", ),
                         attrs={"units": "degC"})
        fraction = xr.DataArray([1.0, 1.0, 0.0, 0.0],
                                dims=("tas", ),
                                coords={"tas": t})

        # Multiply precip by snowfall fraction
        prsn = pr * fraction.interp(tas=tas, method="linear")

    elif method == "auer":
        dtas = convert_units_to(tas, "degK") - convert_units_to(thresh, "degK")

        # Create nodes for the snowfall fraction: -inf, thresh, ..., thresh+6, inf [degC]
        t = np.concatenate([[-273.15],
                            np.linspace(0, 6, 100, endpoint=False), [6, 1e10]])
        t = xr.DataArray(t, dims="tas", name="tas", coords={"tas": t})

        # The polynomial coefficients, valid between thresh and thresh + 6 (defined in CLASS)
        coeffs = xr.DataArray(
            [100, 4.6664, -15.038, -1.5089, 2.0399, -0.366, 0.0202],
            dims=("degree", ),
            coords={"degree": range(7)},
        )

        fraction = xr.polyval(t.tas, coeffs).clip(0, 100) / 100
        fraction[0] = 1
        fraction[-2:] = 0

        # Convert snowfall fraction coordinates to native tas units
        prsn = pr * fraction.interp(tas=dtas, method="linear")

    else:
        raise ValueError(
            f"Method {method} not one of 'binary', 'brown' or 'auer'.")

    prsn.attrs["units"] = pr.attrs["units"]
    return prsn
Пример #14
0
        order=trend_order,
        year_min=1850,
        year_max=2100,
        conv_pic_hist=conv_pic_hist,
        gap=None,
        rmv_disc=False,
        verbose=verbose)

    try:
        # This breaks when Trend_pic_coeff does not contain values.
        # It hapens for BCC-CSM2-MR for which polyfit does not return
        # coefficients but does not crash
        test = Trend_pic_coeff.values

        # Build polynomial from coefficients
        Trend_pic = xr.polyval(coord=y_ds.time, coeffs=Trend_pic_coeff)

        # Remove the average over the reference period
        Trend_pic = Trend_pic - Trend_pic.sel(
            time=slice(ref_p_min, ref_p_max)).mean(dim='time')
    except:
        print(
            '!!! WARNING: Detrending from piControl for this model does not' +
            ' work !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
        branching_method = 'no_detrending'
        Trend_pic = xr.DataArray(np.zeros(len(years_s)),
                                 coords=[years_s],
                                 dims=["time"])

    MAT_CorrectedZOS_reg = np.zeros(
        [len(years_s), len(mask_ds.lat),
Пример #15
0
def odyn_cmip(SCE, data_dir, LOC, ref_steric, ye, N, ys, Gam, NormD, LowPass,
              BiasCorr):
    '''Read the CMIP5 and CMIP6 global steric and ocean dynamics contribution
    and compute a probability distribution.'''

    nb_y2 = ye - ys + 1

    # Read global steric
    full_st_da = misc.read_zostoga_ds(data_dir, SCE)
    # Convert from m to cm
    MAT_G = full_st_da['zostoga_corrected'].sel(
        time=slice(ref_steric[0], ye)) * 100

    if LOC:
        lat_N, lat_S, lon_W, lon_E = LOC

        zos_ds = misc.read_zos_ds(data_dir, SCE)

        full_sd_da = zos_ds['CorrectedReggrided_zos'].sel(
            time=slice(ref_steric[0], ye),
            lat=slice(lat_S, lat_N),
            lon=slice(lon_W, lon_E))

        # There are more models available for zos than for zostoga
        # Here we select the intersection
        model_list = list(
            set(full_sd_da.model.values) & set(MAT_G.model.values))
        MAT_A = full_sd_da.sel(model=model_list).mean(dim=['lat', 'lon'])

        if BiasCorr:
            # Use a bias correction based on the comparison of model and
            # observations between 1979 and 2018
            MAT_A = MAT_A * BiasCorr

        MAT = MAT_A + MAT_G

    else:
        MAT = MAT_G
        MAT_A = xr.zeros_like(MAT_G)

    if LowPass:
        new_time = xr.DataArray(np.arange(ys, ye + 1) + 0.5,
                                dims='time',
                                coords=[np.arange(ys, ye + 1) + 0.5],
                                name='time')
        fit_coeff = MAT.polyfit('time', 2)
        MAT = xr.polyval(coord=new_time, coeffs=fit_coeff.polyfit_coefficients)
        fit_coeff = MAT_G.polyfit('time', 2)
        MAT_G = xr.polyval(coord=new_time,
                           coeffs=fit_coeff.polyfit_coefficients)
        MAT_A = MAT - MAT_G

    list_da = [MAT, MAT_G, MAT_A]

    X_O_out = np.zeros([3, N, nb_y2])

    for idx, da in enumerate(list_da):
        # Select years after the reference period
        da = da.sel(time=slice(ys, None))
        if not (LowPass):
            # Add a year at the end because data is available until 2099 while code
            # goes up to 2100
            dal = da.isel(time=-1).assign_coords(
                time=da.time[-1] + 1)  # Last year with new coords
            da = xr.concat([da, dal], dim='time')

        X_O_out[idx, :, :] = misc.normal_distrib(da, Gam, NormD)

    return X_O_out
def detrend_dim(da, dim, deg=1):
    # detrend along a single dimension
    p = da.polyfit(dim=dim, deg=deg)
    fit = xr.polyval(da[dim], p.polyfit_coefficients)
    return da - fit
            attrs = {'units': hist_ds.attrs['parent_time_units']}
            time_flt = [float(hist_ds.attrs['branch_time_in_parent'])]
            time_ds = xr.Dataset({'time': ('time', time_flt, attrs)})
            time_ds = xr.decode_cf(time_ds, use_cftime=True)
            conv_pic_hist = float(VAR1a.time[0]) - time_ds.time.dt.year.values[0]
    except:
        # Pick a random large value that makes sure branching is not used in
        # trend_zos_pic_cmip5
        conv_pic_hist = -9999
        
    Trend_pic_coeff, branching_method = pic.trend_pic(
        MIP, VAR, ModelList.iloc[i], order=1, year_min=1850, year_max=2100,
        conv_pic_hist=conv_pic_hist, gap=gap, rmv_disc=True, verbose=verbose)
    
    # Build polynomial from coefficients
    Trend_pic = xr.polyval(coord=VAR1a.time, coeffs=Trend_pic_coeff)

    trend_da[i,:] = Trend_pic.sel(time=slice(year_min_min,year_max))
    da[i,:] = VAR1a.sel(time=slice(year_min_min,year_max))
    
ds[VAR+'_corrected'] = da
ds['trend_picontrol'] = trend_da

### Remove missing models
ds = ds.where(ds.zostoga_corrected!=0)
ds = ds.dropna('model',how='all')

if year_max == 2300: #TODO Old code bellow
    print('### List of models that run to 2300')
    for i in range(0,dimMod-1):
        tot_mis = np.sum(np.isnan(AVAR1[0,i,:]))