Sentinel-3 Land Surface Temperature
- Instrument: S3 SLSTR
- Product name and level: LST L2
- Spatial resolution: 1 km
- Temporal resolution: ~ 1 day
- Documentation: https://sentiwiki.copernicus.eu/web/slstr-products#S3-SLSTR-Products-L2-LST-Products
Sentinel-3 Chlorophyll Terrestrial Index
- Instrument: S3 OLCI
- Product name and level: OTCI L2
- Spatial resolution: 300 m
- Temporal resolution: ~ 1 day
- Documentation: https://sentiwiki.copernicus.eu/web/olci-products#S3-OLCI-Products-L2-Land-OCTI
Spatial resampling and regridding to 2.4 km using Sentinel-5p product as reference and the xESMF package
Description
User define function where the input are S3-SIF, S3-LST, S3-OTCI, and S3-IWV The function use a spatial moving window and optimize several parameters of an empirical model using the data in the moving window. below the function and the parameters used when using local xarray cubes
Function
PythonCollapsed Code1def optimize_params_window(sif_w, ogvi_w, ndwi_w, lst_w,2param_ini, param_bounds, min_obs):3"""4Wrapper function to optimize SIF parameters for a single window.5Designed to be used with apply_ufunc or iteration.6Assumes input arrays (sif_w, etc.) are 2D numpy arrays (window data).7"""8# Flatten the window arrays and filter out NaNs9# Important: Filter consistently across all variables10mask = ~np.isnan(sif_w) & ~np.isnan(ogvi_w) & ~np.isnan(ndwi_w) & ~np.isnan(lst_w)11n_valid = np.sum(mask)1213if n_valid < min_obs:14# Not enough valid observations in the window15return np.full(len(param_ini), np.nan, dtype=np.float32)161718sif_obs_f = sif_w[mask]19vi_f = ogvi_w[mask]20et_f = ndwi_w[mask] # Using NDWI as proxy for water stress/ET effect21lst_f = lst_w[mask]2223# Define bounds for L-BFGS-B24bounds_scipy = list(zip(*param_bounds)) # [(min1, max1), (min2, max2), ...]252627result = minimize(cost_function,28x0=param_ini,29args=(vi_f, et_f, lst_f, sif_obs_f),30method='L-BFGS-B',31bounds=bounds_scipy,32options={'maxiter': 1000, 'ftol': 1e-7, 'gtol': 1e-5}) # Adjust options as needed3334optimized_params = np.array(np.clip(result.x, param_bounds[0], param_bounds[1]))35#print(optimized_params.dtype)36#print(optimized_params.size)37#print(optimized_params.shape)38return optimized_params
Example using xarray
PythonCollapsed Code1# Define optimization parameters and bounds2param_ini = np.array([1.0, 2.0, -295.0, 10.0])3param_min = np.array([0.5, 0.1, -310.0, 1.0])4param_max = np.array([1.5, 5.0, -290.0, 50.0])5param_bounds = np.array([param_min, param_max])67min_obs_optim = 21 # Minimum valid observations in window8window_size_optim = 5 # Must match Julia 'window_edge'910# using apply_ufunc11parameters_cube = xr.apply_ufunc(12optimize_params_window, # Function to apply13# Input arrays:14sif_cube_low_july['SIF_743'].rolling(lat=window_size_lat, lon=window_size_lon, center=True).construct(lat = 'lat_roll', lon = 'lon_roll'),15ogvi_cube_low_july['OGVI'].rolling(lat=window_size_lat, lon=window_size_lon, center=True).construct(lat = 'lat_roll', lon = 'lon_roll'),16ndwi_cube_low_july['NDWI'].rolling(lat=window_size_lat, lon=window_size_lon, center=True).construct(lat = 'lat_roll', lon = 'lon_roll'),17lst_cube_low_july['LST'].rolling(lat=window_size_lat, lon=window_size_lon, center=True).construct(lat = 'lat_roll', lon = 'lon_roll'),18# Keyword arguments for the function:19kwargs={'param_ini': param_ini, 'param_bounds': param_bounds, 'min_obs': min_obs_optim},20# Define input core dimensions (the window dimensions):21input_core_dims=[['lat_roll', 'lon_roll'], ['lat_roll', 'lon_roll'], ['lat_roll', 'lon_roll'], ['lat_roll', 'lon_roll']],22# Define output core dimensions (the parameters dimension):23output_core_dims=[['parameters']],24dask_gufunc_kwargs={'output_sizes': {'parameters': 6}},25# Specify the parameters dimension coordinates:26dask='parallelized', # Enable Dask parallelization27output_dtypes=[np.float64], # Specify output data type28vectorize = True,29# Add a new dimension 'parameters' to the output30exclude_dims=set(('lat_roll', 'lon_roll')), # Exclude window dims from output31).rename('optimized_parameters')3233# Assign coordinates to the new 'parameters' dimension3435parameters_cube['parameters'] = ["b1", "b2", "b3", "b4", "b5", "b6"]36parameters_cube = parameters_cube.chunk({'lat': 80, 'lon': 80, 'parameters': 6}) # Rechunk37
- Bounding Box: Europe
- Time period: 8 daily composite
Spatial resampling and regridding to 2.4 km using Sentinel-5p product as reference and the xESMF package
Spatial resampling and regridding to 1km km using S3-LST product as reference and the xESMF package
Spatial resampling and regridding to 1km km using S3-LST product as reference and the xESMF package
Spatial resampling and regridding to 2.4 km using Sentinel-5p product as reference and the xESMF package
- Bounding Box: Europe
- Time period: 8 days composite
- Bounding Box: Europe
- Time period: 8 daily composite
Description
Interpolate the parameters cube to 1 km
Code example
PythonCollapsed Code1parameters_cube_high = parameters_cube.interp_like(2lst_cube_high_july, # Use one of the high-res grids as template3method="linear", # Options: 'linear', 'nearest'4#kwargs={"fill_value": "extrapolate"} # Optional: handle edges5# Or fill_value=np.nan if extrapolation is not desired6# kwargs = {'fill_value':np.nan},7).chunk({'lat': 400, 'lon': 400, 'parameters': -1})
Parameters cube at 1 km resolution
Description
Predicting SIF at 1 km using the parameters, the SIF prediction, model and all predictors at 1 km using a spatial moving window
Code example
PythonCollapsed Code1# Define the wrapper function for downscaling within a window2def sif_downscaling_window(ogvi_hw, lst_hw, ndwi_hw, params_hw):3"""4Calculates downscaled SIF for a central pixel based on window means.5Inputs are 3D numpy arrays (window_x, window_y, [parameters]).6"""789# Calculate mean parameters within the window (ignore NaNs)10# Need to handle case where all params in window are NaN11mean_params = np.nanmean(params_hw, axis=(0,1))1213if np.all(np.isnan(mean_params)):14return np.array([np.nan]) # Cannot calculate if no valid parameters in window)1516# Calculate mean predictors within the window (ignore NaNs)17mean_ogvi = np.nanmean(ogvi_hw)18mean_ndwi = np.nanmean(ndwi_hw)19mean_lst = np.nanmean(lst_hw)2021# Check if any mean predictor is NaN (means all values in window were NaN)22if np.isnan(mean_ogvi) or np.isnan(mean_lst):23return np.array([np.nan])2425# Apply SIF model using mean values26sif_ds = sif_model(mean_ogvi, mean_lst, mean_params)27return np.array([sif_ds])2829## applying the function to predict SIF3031window_size_downscale = 33233sif_cube_high = xr.apply_ufunc(34sif_downscaling_window,35# Input arrays with rolling windows constructed36ogvi_cube_high_july['OGVI'].rolling(lat=window_size_downscale, lon=window_size_downscale, center=True).construct(lat = 'lat_roll', lon = 'lon_roll'),37ndwi_aligned.rolling({x_dim: window_size_downscale, y_dim: window_size_downscale}, center=True).construct(x_dim+'_w', y_dim+'_w'),38lst_cube_high_july['LST'].rolling(lat=window_size_downscale, lon=window_size_downscale, center=True).construct(lat = 'lat_roll', lon = 'lon_roll'),39parameters_cube_high['optimized_parameters'].rolling(lat=window_size_downscale, lon=window_size_downscale, center=True).construct(lat = 'lat_roll', lon = 'lon_roll').chunk({'parameters':-1}),40# Input core dimensions now include the window dims and the parameter dim for the last input41input_core_dims=[['lat_roll', 'lon_roll'],42['lat_roll', 'lon_roll'],43['lat_roll', 'lon_roll', 'parameters']],44# Output is scalar for each pixel (no core dims)45output_core_dims=[['SIF_downscaled']],46dask_gufunc_kwargs={'output_sizes': {'SIF_downscaled': 1}},47dask='parallelized',48output_dtypes=[np.float64],49vectorize = True,50exclude_dims=set(('lat_roll', 'lon_roll')), # Exclude window dims from output51).rename('SIF_downscaled')5253sif_cube_high = sif_cube_high.to_dataset(name = 'SIF')5455sif_cube_high.attrs['description'] = f'Downscaled SIF using Duveiller&Cescatti method, {window_size_downscale}x{window_size_downscale} window smoothing'5657
- Bounding Box: Europe
- Time period: 8 days composite
Save SIF downscaled at 1 km as .zarr
Contact S5p-PAL support!! (ASAP)
Re-make stac catalog tihs week to prototype
Deploy the STAC somewhere
Sentinel-3 Water Vapour Column over Land
- Instrument: S3 OLCI
- Product name and level: IWV L2
- Spatial resolution: 300 m
- Temporal resolution: ~1 day
- Documentation: https://sentiwiki.copernicus.eu/web/olci-products#S3-OLCI-Products-L2-Land-IWV
Sentinel-5p Sun Induced Fluorescence Dong Li
- Instrument: Sentinel-5p TROPOMI
- Product name and level: SIF L3 (Gridded)
- Spatial resolution: ~ 2.4 km Ungridded
- Temporal resolution: 1 day
- Product locations: https://github.com/lidongmath/Sentinel-5P-TROPOMI-ANNSIF?tab=readme-ov-file
- Documentation: https://www.sciencedirect.com/science/article/pii/S0034425725003918
Potential improvement:
SIF / NIRv (From Sentinel-2)
SIF / (fapar Sentinel-3 * Solar radiation agERA5)
Save parameters cube as .zarr
Sentinel-5p Sun Induced Fluorescence
- Instrument: Sentinel-5p TROPOMI
- Product name and level: SIF L3 (Gridded)
- Spatial resolution: ~ 2.4 km
- Temporal resolution: 1 day
- Product STAC Catalog: STAC
- Documentation: https://data-portal.s5p-pal.com/