Creating meps_example_reduced#
This notebook outlines how the small-size test dataset meps_example_reduced was created based on the slightly larger dataset meps_example. The zipped up datasets are 263 MB and 2.6 GB, respectively. See README.md for info on how to download meps_example.
The dataset was reduced in size by reducing the number of grid points and variables.
# Standard library
import os
# Third-party
import numpy as np
import torch
The number of grid points was reduced to 1/4 by halving the number of coordinates in both the x and y direction. This was done by removing a quarter of the grid points along each outer edge, so the center grid points would stay centered in the new set.
# Load existing grid
grid_xy = np.load('data/meps_example/static/nwp_xy.npy')
# Get slices in each dimension by cutting off a quarter along each edge
num_x, num_y = grid_xy.shape[1:]
x_slice = slice(num_x//4, 3*num_x//4)
y_slice = slice(num_y//4, 3*num_y//4)
# Index and save reduced grid
grid_xy_reduced = grid_xy[:, x_slice, y_slice]
np.save('data/meps_example_reduced/static/nwp_xy.npy', grid_xy_reduced)
This cut out the border, so a new perimeter of 10 grid points was established as border (10 was also the border size in the original “meps_example”).
# Outer 10 grid points are border
old_border_mask = np.load('data/meps_example/static/border_mask.npy')
assert np.all(old_border_mask[10:-10, 10:-10] == False)
assert np.all(old_border_mask[:10, :] == True)
assert np.all(old_border_mask[:, :10] == True)
assert np.all(old_border_mask[-10:,:] == True)
assert np.all(old_border_mask[:,-10:] == True)
# Create new array with False everywhere but the outer 10 grid points
border_mask = np.zeros_like(grid_xy_reduced[0,:,:], dtype=bool)
border_mask[:10] = True
border_mask[:,:10] = True
border_mask[-10:] = True
border_mask[:,-10:] = True
np.save('data/meps_example_reduced/static/border_mask.npy', border_mask)
A few other files also needed to be copied using only the new reduced grid
# Load surface_geopotential.npy, index only values from the reduced grid, and save to new file
surface_geopotential = np.load('data/meps_example/static/surface_geopotential.npy')
surface_geopotential_reduced = surface_geopotential[x_slice, y_slice]
np.save('data/meps_example_reduced/static/surface_geopotential.npy', surface_geopotential_reduced)
# Load pytorch file grid_features.pt
grid_features = torch.load('data/meps_example/static/grid_features.pt')
# Index only values from the reduced grid.
# First reshape from (num_grid_points_total, 4) to (num_grid_points_x, num_grid_points_y, 4),
# then index, then reshape back to new total number of grid points
print(grid_features.shape)
grid_features_new = grid_features.reshape(num_x, num_y, 4)[x_slice,y_slice,:].reshape((-1, 4))
# Save to new file
torch.save(grid_features_new, 'data/meps_example_reduced/static/grid_features.pt')
# flux_stats.pt is just a vector of length 2, so the grid shape and variable changes does not change this file
torch.save(torch.load('data/meps_example/static/flux_stats.pt'), 'data/meps_example_reduced/static/flux_stats.pt')
The number of variables was reduced by truncating the variable list to the first 8.
num_vars = 8
# Load parameter_weights.npy, truncate to first 8 variables, and save to new file
parameter_weights = np.load('data/meps_example/static/parameter_weights.npy')
parameter_weights_reduced = parameter_weights[:num_vars]
np.save('data/meps_example_reduced/static/parameter_weights.npy', parameter_weights_reduced)
# Do the same for following 4 pytorch files
for file in ['diff_mean', 'diff_std', 'parameter_mean', 'parameter_std']:
old_file = torch.load(f'data/meps_example/static/{file}.pt')
new_file = old_file[:num_vars]
torch.save(new_file, f'data/meps_example_reduced/static/{file}.pt')
Lastly the files in each of the directories train, test, and val have to be reduced. The folders all have the same structure with files of the following types:
nwp_YYYYMMDDHH_mbrXXX.npy
wtr_YYYYMMDDHH.npy
nwp_toa_downwelling_shortwave_flux_YYYYMMDDHH.npy
with YYYYMMDDHH being some date with hours, and XXX being some 3-digit integer.
The first type of file has x and y in dimensions 1 and 2, and variable index in dimension 3. Dimension 0 is unchanged. The second type has has x and y in dimensions 1 and 2. Dimension 0 is unchanged. The last type has just x and y as the only 2 dimensions.
print(np.load('data/meps_example/samples/train/nwp_2022040100_mbr000.npy').shape)
print(np.load('data/meps_example/samples/train/nwp_toa_downwelling_shortwave_flux_2022040112.npy').shape)
(65, 268, 238, 18)
(65, 268, 238)
The following loop goes through each file in each sample folder and indexes them according to the dimensions given by the file name.
for sample in ['train', 'test', 'val']:
files = os.listdir(f'data/meps_example/samples/{sample}')
for f in files:
data = np.load(f'data/meps_example/samples/{sample}/{f}')
if 'mbr' in f:
data = data[:,x_slice,y_slice,:num_vars]
elif 'wtr' in f:
data = data[x_slice, y_slice]
else:
data = data[:,x_slice,y_slice]
np.save(f'data/meps_example_reduced/samples/{sample}/{f}', data)
Lastly, the file data_config.yaml is modified manually by truncating the variable units, long and short names, and setting the new grid shape. Also the unit descriptions containing ^ was automatically parsed using latex, and to avoid having to install latex in the GitHub CI/CD pipeline, this was changed to **.
This new config file was placed in data/meps_example_reduced, and that directory was then zipped and placed in a European Weather Cloud S3 bucket.