Precipitation Nowcasting with Machine Learning (2026 Guide)
Updated on December 05, 2025 12 minutes read
Modern weather prediction is more accurate than ever, but local details can still be tricky. A five-day forecast might tell you that rain is likely, yet it will not always capture exactly when a shower reaches your street or runway.
Precipitation nowcasting focuses on the next few hours. It uses recent radar or satellite images, combined with machine learning, to predict how rain fields will move, grow, or decay in the very near future.
In this guide, you will build a radar-based precipitation nowcasting pipeline with a U-Net style architecture and a pretrained model called RainNet. The goal is to understand the main design choices so you can adapt the approach to other regions or datasets in 2026.
What Is Precipitation Nowcasting
Nowcasting usually covers the next zero to six hours. It is especially useful for aviation, road safety, flood warning, and renewable energy, where minute-scale timing and location of rain can strongly affect decisions.
Traditional numerical weather prediction models simulate atmospheric physics on large grids. They work well from several hours to many days ahead, but they are computationally expensive and relatively coarse in space and time.
Data-driven approaches learn directly from past observations, such as radar composites. They update quickly, run efficiently on modern hardware, and often perform best in the first hour or two when recent observations contain the most useful information.
Data for Weather and Climate Models
Physics-free models depend on historical data. For precipitation nowcasting, two broad types of data are particularly important: gridded image-like products and station-based tabular records.
In operational systems, these sources are often blended. For this tutorial, you will focus on image data from weather radar, then briefly see how tabular data can be added later as an extension.
Radar and Satellite Image Data
Radar and satellite products are naturally represented as images over a fixed geographical area. Each pixel might encode radar reflectivity, rain rate, cloud top temperature, or another physical quantity, sampled every few minutes.
For example, many national weather services provide radar composites on a kilometre-scale grid with a five-minute temporal resolution. This kind of data is ideal for convolutional neural networks that process images.
By stacking several recent radar images over time, you obtain a three-dimensional input: width, height, and time. A deep model can then learn how structures move and change and can output the expected radar field at one or more future times.
Tabular and Station Data
Tabular data comes from point measurements such as weather stations, buoys, or local sensors. Typical variables include temperature, humidity, wind speed and direction, pressure, and accumulated rainfall.
You can use these features as extra channels or side inputs for a nowcasting model. The main challenge is matching point measurements to the radar grid and handling missing values or noisy sensors.
In practice, it is common to first solve the pure radar nowcasting problem. Once you have a working pipeline based on images alone, you can start to experiment with multimodal extensions that mix radar, satellite, and station data.
From CNNs to U-Net for Weather Maps
Most image-based nowcasting models are built on convolutional neural networks (CNNs). Early architectures such as LeNet, AlexNet, and VGG showed that stacks of convolution and pooling layers can learn visual features directly from data.
These early CNNs ended with fully connected layers, which work well for image classification but not for dense prediction tasks such as segmentation or pixel-wise regression. For nowcasting, you need a model that outputs an entire image of future rain intensities.
Fully convolutional encoder-decoder architectures address this limitation. U-Net is one of the most influential of these models and forms the basis of many modern precipitation nowcasting systems.
Understanding the U-Net Architecture
U-Net was introduced for biomedical image segmentation and quickly spread to other computer vision domains. It is named after its symmetric U-shaped layout, with a downsampling encoder on the left and an upsampling decoder on the right.
Conceptually, a standard two-dimensional U-Net has three main parts:
- Contracting encoder: repeated blocks of convolutional layers followed by max pooling sp gradually,atial resolution gradually, while increasing the number of feature channels.
- Bottleneck: at the bottom of the U, convolutional blocks operate at the smallest spatial resolution and capture global context across the full image.
- Expanding decoder: transpose convolution or upsampling layers increase the spatial resolution stepEPEPy step, ep and usually reduce the number of channels.
The distinctive feature of U-Net is the use of skip connections between the encoder and decoder. Feature maps from the encoder are concatenated with upsampled decoder outputs at matching resolutions so that fine spatial details are not lost.
These skip connections are particularly important for precipitation nowcasting. Rain cells can be small and intense, so preserving sharp gradients and local structure helps the model produce realistic, high-resolution forecasts.
Building a U-Net Model for Radar Nowcasting
You will now walk through a reference implementation of a U-Net model for radar-based precipitation nowcasting using TensorFlow and Keras. The main steps are:
- Install and import the required libraries.
- Download recent radar composites from an open data server.
- Visualise the input data.
- Define modular U-Net building blocks.
- Construct the full U-Net model.
- Load pretrained RainNet weights.
- Preprocess inputs, run predictions, and postprocess outputs.
This structure is inspired by the RainNet project, which uses a U-Net-like encoder-decoder network trained on several years of national radar composites.
1. Installing Dependencies
First, install wradlib, an open source library for weather radar data processing, plus standard scientific Python tools and TensorFlow. In a notebook, you can run:
!pip install wradlib tensorflow numpy matplotlib imageio
Then import the main dependencies:
import io
import datetime as dt
import urllib.request
import imageio
import matplotlib.pyplot as plt
import numpy as np
import wradlib as wrl
From tensorflow. Keras. layers import (
Input,
Conv2D,
Activation,
Concatenate,
Conv2DTranspose,
MaxPool2D,
)
From tensorflow. Keras. Models import Model
Library versions and APIs evolve, so if any import fails in 2026, check the latest wradlib and TensorFlow documentation for updated module paths and function names.
2. Downloading Radar Data
In this example,u will work with near-real-time radar composites. The helper functions below download the most recent radar scan and three earlier scans that are five minutes apart, yielding four input frames.
def download_and_read_ry(timestamp):
url = (
"https://opendata.dwd.de/weather/radar/radolan/ry/"
f"raa01-ry_10000-{timestamp}-dwd---bin"
)
data_binary = urllib.request.urlopen(url).read()
data, attrs = wrl.io.read_radolan_composite(
io.BytesIO(data_binary),
missing=0,
)
Data = data.astype("float32")
Return data, attrs
def download_latest_scans():
latest_scan, latest_attrs = download_and_read_ry("latest")
latest_dt = latest_attrs["datetime"]
offsets = [15, 10, 5]
stamps = [
(latest_dt - dt.timedelta(minutes=offset)).strftime("%y%m%d%H%M")
for offset in offsets
]
previous_scans = np.array(
[download_and_read_ry(ts)[0] for ts in stamps]
)
scans = np.concatenate(
[previous_scans, latest_scan[np.newaxis, :, :]],
axis=0,
)
return scans, latest_dt
Public data endpoints sometimes change structure over time. If you see a 404 or parsing error, list the directory in a browser or with a command-line tool and adapt the URL template to the current naming scheme.
Call the helper to fetch the latest four radar images:
ry_latest, ry_latest_timestamp = download_latest_scans()
3. Visualising Radar Composites
Before training or running a model, always check that the data looks sensible. Wradlib provides convenient plotting utilities for radar products.
for i in range(ry_latest.shape[0]):
wrl.vis.plot_ppi(ry_latest[i])
plt.show()
You should see four frames of radar reflectivity or rain rate that progress in time. These will form the input sequence for the nowcasting model.
4. Defining U-Net Building Blocks
To keep the architecture readable, define three reusable building blocks: a pure convolutional block, an encoder block, and a decoder block. These map directly to components in many U-Net tutorials.
def conv_block(x, num_filters):
x = Conv2D(
num_filters,
kernel_size=3,
padding="same",
kernel_initializer="he_normal",
)(x)
x = Activation("relu")(x)
x = Conv2D(
num_filters,
kernel_size=3,
padding="same",
kernel_initializer="he_normal",
)(x)
x = Activation("relu")(x)
return x
def encoder_block(x, num_filters):
x = conv_block(x, num_filters)
p = MaxPool2D(pool_size=(2, 2))(x)
return x, p
def decoder_block(x, skip_features, num_filters):
x = Conv2DTranspose(
num_filters,
kernel_size=(2, 2),
strides=2,
padding="same",
kernel_initializer="he_normal",
)(x)
x = Concatenate()([x, skip_features])
x = conv_block(x, num_filters)
return x
Compared with many quick code snippets, this version fixes common mistakes such as missing layer calls and inconsistent block names. It also keeps the number of filters and kernel sizes easy to tweak.
5. Building the Full U-Net Model
With the blocks defined, you can assemble a four-level U-Net that accepts four radar frames stacked along the channel dimension and outputs a single rain rate image.
def build_unet(input_shape=(928, 928, 4)):
inputs = Input(input_shape)
e1, p1 = encoder_block(inputs, 64)
e2, p2 = encoder_block(p1, 128)
e3, p3 = encoder_block(p2, 256)
e4, p4 = encoder_block(p3, 512)
b1 = conv_block(p4, 1024)
d1 = decoder_block(b1, e4, 512)
d2 = decoder_block(d1, e3, 256)
d3 = decoder_block(d2, e2, 128)
d4 = decoder_block(d3, e1, 64)
outputs = Conv2D(
1,
kernel_size=1,
padding="same",
activation="linear",
)(d4)
model = Model(inputs, outputs, name="unet_rain_nowcasting")
return model
You can experiment with deeper or shallower models, different activation functions, or additional input channels, as long as you keep the encoder and decoder symmetric and maintain the skip connections.
6. Loading Pretrained RainNet Weights
Training a model like this from scratch on several years of radar data is computationally expensive. The RainNet project provides a U-Net-inspired architecture and pretrained weights that you can reuse.
Clone the repository and download the weights, for example, in a Colab notebook:
!git clone https://github.com/hydrogo/rainnet.git
!wget -O /content/rainnet/rainnet_weights.h5 \
https://zenodo.org/record/3630429/files/rainnet_weights.h5
Then construct the RainNet model as implemented in the repository and load the weights:
import sys
sy s.path.append("/content/rainnet")
from rainnet import rainnet
model = rainnet.rainnet()
model.load_weights("/content/rainnet/rainnet_weights.h5")
model.summary()
If you decide to use your custom U-Net instead, you must ensure that its architecture matches the saved weights exactly or retrain the network on your own radar dataset.
7. Preprocessing and Postprocessing Radar Fields
RainNet uses a simple preprocessing pipeline: convert radar intensities to a log scale to reduce skew, pad the 900 by 900 grid to 928 by 928, and stack frames into a batch-friendly tensor.
def scaler(array):
return np.log(array + 0.01)
def inverse_scaler(array):
return np.exp(array) - 0.01
def pad_to_shape(array, from_shape=900, to_shape=928, mode="reflect"):
padding = int((to_shape - from_shape) / 2)
if mode == "constant":
array_padded = np.pad(
array,
((0, 0), (padding, padding), (padding, padding), (0, 0)),
mode="constant",
constant_values=0,
)
Else:
array_padded = np.pad(
array,
((0, 0), (padding, padding), (padding, padding), (0, 0)),
mode=mode,
)
return array_padded
def crop_to_shape(array, from_shape=928, to_shape=900):
padding = int((from_shape - to_shape) / 2)
return array[:, padding:padding + to_shape, padding:padding + to_shape]
Next, define convenience functions that prepare the model input and convert the predictions back to the original radar grid.
def preprocess_inputs(x):
# Move time axis to channels and add a batch dimension
x = np.moveaxis(x, 0, -1)
x = x[np.newaxis, :, :, :]
# Log scale to reduce skew towards large values
x = scaler(x)
# Pad from 900x900 to 928x928
x = pad_to_shape(x)
return x
def postprocess_outputs(predictions):
# Convert list of predictions to array and squeeze batch dimension
preds = np.squeeze(np.array(predictions))
# Undo log transform
preds = inverse_scaler(preds)
# Crop back to 900x900
preds = crop_to_shape(preds)
# Remove negative values
preds = np.where(preds > 0, preds, 0)
return preds
The exact scaling constants and padding size depend on how the pretrained model was trained. Always cross-check these steps with the original RainNet code or paper if you adapt the functions.
8. Running Recursive Nowcasts
RainNet is usually run recursively. The model predicts a single step, then that prediction is appended to the input sequence to generate the next step, and so on.
def predict_nowcast(model_instance, input_data, lead_time=24):
x = preprocess_inputs(input_data)
forecasts = []
for _ in range(lead_time):
pred = model_instance.predict(x)
forecasts.append(pred)
# Drop the oldest frame and append the new prediction
x = np.concatenate([x[:, :, :, 1:], pred], axis=-1)
return postprocess_outputs(forecasts)
If each step corresponds to five minutes, a lead time of 24 yields a two-hour nowcast sequence. You can adjust this depending on your application and how well the model behaves at longer horizons.
Call the function on the latest radar scans:
y_pred = predict_nowcast(model, ry_latest, lead_time=24)
9. Visualising Predictions as an Animated GIF
Finally, create a simple animation to inspect how the forecast evolves. Each frame in the GIF corresponds to one future step.
frames = []
filenames = []
for i in range(y_pred.shape[0]):
fig = plt.figure()
wrl.vis.plot_ppi(y_pred[i])
plt.title(f"Lead time step {i + 1}")
filename = f"/content/nowcast_{i:02d}.png"
plt.savefig(filename)
plt.close(fig)
filenames.append(filename)
frames.append(imageio.imread(filename))
imageio.mimsave("/content/nowcast.gif", frames, fps=4)
You can then download or embed the generated GIF in a notebook or web page and assess how well the model tracks and intensifies precipitation structures.
Beyond U-Net in 2026
Since RainNet and early U-Net-based nowcasting models were published, research has moved quickly. New variants add attention mechanisms, depthwise separable convolutions, or transformer-style components to capture both local detail and long-range dependencies.
There is also growing interest in global AI weather models that combine deep learning with traditional numerical prediction systems. These hybrids aim to deliver fast, high-resolution forecasts while still respecting physical constraints.
For learning and experimentation, U-Net style models such as RainNet remain a great starting point. They are conceptually simple, widely used in many domains, and well supported in current deep learning frameworks.
Practical Considerations and Limitations
When you apply RainNet or a similar model, keep the following points in mind:
- Spatial coverage: Pretrained weights are usually trained on a specific radar network, so performance may degrade in other regions without retraining.
- Temporal resolution: the training timestep matters. If your data has a different cadence, adjust preprocessing or retrain the model.
- Extreme events: models trained on historical data can struggle with rare extremes that are underrepresented. Validate carefully in heavy rainfall cases.
- Operations: For real-time use, you must handle missing data, latency, and monitoring, not just the neural network itself.
In practice, nowcasting models are often combined with numerical forecasts and expert meteorological judgement rather than used in isolation.
Conclusion
In this tutorial, you built a practical precipitation nowcasting pipeline based on radar data, a U-Net style architecture, and the RainNet pretrained model. You saw how to download and visualise radar composites, define modular blocks, and generate multi-step forecasts.
From here, you can fine-tune the model on your own region, add attention mechanisms or transformer blocks, or extend the pipeline with satellite and station data. The core ideas of convolutional encoders, skip connections, and careful preprocessing will continue to be useful in 2026 and beyond.
If you would like to turn this type of project into part of a professional portfolio, structured learning can help. An intensive data science and AI program gives you guidance, deadlines, and feedback while you build real-world projects.
Learn and Experiment with Code Labs Academy
Interested in going further with machine learning, deep learning, and computer vision? Code Labs Academy offers an online Data Science and AI Bootcamp with flexible full-time and part-time options for learners who want to reskill or upskill for industry roles.
We also host regular free events and workshops where you can explore topics such as neural networks, time series forecasting, and model deployment in a live, hands-on format. Many of these sessions use examples similar to the nowcasting pipeline you have just explored.