Models¶
BaseModel
¶
Bases: LightningModule, ABC
A base class for all models used in the CryoCast project.
ignored_hparams = frozenset(('latitudes_fn', 'longitudes_fn', 'mask_dir', 'metrics'))
class-attribute
¶
name = name
instance-attribute
¶
hemisphere = Hemisphere(hemisphere)
instance-attribute
¶
latitudes_fn = latitudes_fn
instance-attribute
¶
longitudes_fn = longitudes_fn
instance-attribute
¶
channel_names = list(channel_names) if channel_names else []
instance-attribute
¶
checkpoint_epoch = None
instance-attribute
¶
training_metadata = None
instance-attribute
¶
n_forecast_steps = n_forecast_steps
instance-attribute
¶
n_history_steps = n_history_steps
instance-attribute
¶
input_spaces = [DataSpace.from_dict(space) for space in input_spaces]
instance-attribute
¶
output_space = DataSpace.from_dict(output_space)
instance-attribute
¶
target_variable_indices = list(target_variable_indices or [])
instance-attribute
¶
optimizer_cfg = optimizer
instance-attribute
¶
scheduler_cfg = scheduler
instance-attribute
¶
lr_scheduler_cfg = lr_scheduler
instance-attribute
¶
loss_cfg
property
writable
¶
Get the loss configuration.
metric_cfgs = {}
instance-attribute
¶
test_metrics = self.build_metrics(land_mask)
instance-attribute
¶
train_metrics = self.build_metrics(land_mask)
instance-attribute
¶
validation_metrics = self.build_metrics(land_mask)
instance-attribute
¶
climatology_metrics = self.build_metrics(land_mask)
instance-attribute
¶
latitudes
cached
property
¶
longitudes
cached
property
¶
multistage_only
property
¶
build_metrics(land_mask)
¶
Build a metric collection from the configured metrics.
Each configured metric is a mapping with a name (its key in logs), a Hydra
_target_ and any other constructor arguments, e.g.
{"name": "my_metric", "_target_": "my_package.MyMetric", "k": 3}. Targets
that subclass LandMaskMixin are also given the land mask.
This should include only metrics that are compatible with the output space. We therefore filter out single-channel metrics from the metric collection if the model will predict multiple channels.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
land_mask
|
Tensor | None
|
Optional boolean tensor of shape (H, W), True for ocean cells and
False for land. This is passed to every |
required |
Returns:
| Type | Description |
|---|---|
MetricCollection
|
A MetricCollection containing the requested metrics. |
configure_optimizers()
¶
Construct the optimizer and optional scheduler from the config.
forward(inputs)
abstractmethod
¶
Forward step of the model.
- start with multiple
NTCHWinputs, one for each input dataset - return a single
NTCHWoutput representing the predicted output
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
inputs
|
dict[str, TensorNTCHW]
|
Dictionary of dataset name to TensorNTCHW with shape (batch, n_history_steps, C_input_k, H_input_k, W_input_k) |
required |
Returns:
| Type | Description |
|---|---|
TensorNTCHW
|
Predicted TensorNTCHW with shape (batch, n_forecast_steps, C_output, H_output, W_output) |
load_state_dict(state_dict, *args, **kwargs)
¶
Load a state dict, ignoring any metric collections it contains.
on_fit_start()
¶
Record the data that this model is being trained on.
on_load_checkpoint(checkpoint)
¶
Restore the training metadata, if the checkpoint has any.
on_save_checkpoint(checkpoint)
¶
Save the training metadata as plain types, so it loads with weights_only.
loss(prediction, target)
¶
Calculate the loss given a prediction and target.
process_batch(batch)
¶
Process a batch before the forward pass and loss computation.
Subclasses can override this to extract or transform inputs before the standard
training/validation steps. The returned dict must include a "target" key.
test_step(batch, _batch_idx)
¶
Run the test step, in PyTorch eval model (i.e. no gradients).
- Separate the batch into inputs and target
- Run inputs through the model
- Update the test metrics (and climatology metrics if appropriate)
- Return the prediction, target and loss
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
batch
|
dict[str, TensorNTCHW]
|
Dictionary mapping dataset name to its contents. There is one entry for each input dataset and one for the target. Each of these is a TensorNTCHW with (batch_size, n_history_steps, C, H, W). |
required |
Returns:
| Type | Description |
|---|---|
ModelStepOutput
|
A ModelStepOutput containing the prediction, target and loss for the batch. |
training_step(batch, _batch_idx)
¶
Run the training step.
- Separate the batch into inputs and target
- Run inputs and target through the model
- Calculate the loss wrt. the target
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
batch
|
dict[str, TensorNTCHW]
|
Dictionary mapping dataset name to its contents. There is one entry for each input dataset and one for the target. Each of these is a TensorNTCHW with (batch_size, n_history_steps, C, H, W). |
required |
Returns:
| Type | Description |
|---|---|
ModelStepOutput
|
A ModelStepOutput containing the prediction, target and loss for the batch. |
validation_step(batch, _batch_idx)
¶
Run the validation step.
A batch contains one tensor for each input dataset and one for the target These are [NTCHW] tensors with (batch_size, n_history_steps, C, H, W)
- Separate the batch into inputs and target
- Run inputs through the model
- Calculate and log the loss wrt. the target
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
batch
|
dict[str, TensorNTCHW]
|
Dictionary mapping dataset name to its contents. There is one entry for each input dataset and one for the target. Each of these is a TensorNTCHW with (batch_size, n_history_steps, C, H, W). |
required |
Returns:
| Type | Description |
|---|---|
ModelStepOutput
|
A ModelStepOutput containing the prediction, target and loss for the batch. |
Climatology
¶
Bases: BaseModel
automatic_optimization = False
instance-attribute
¶
model = nn.Identity()
instance-attribute
¶
configure_optimizers()
¶
Climatology model does not need an optimizer.
forward(inputs)
¶
Forward step of the model.
Return the climatology (calendar-day mean) field for each forecast step,
supplied by the data loader under the climatology key with shape
[batch, n_forecast_steps, C_target, H_target, W_target].
DDPM
¶
Bases: BaseModel
Denoising Diffusion Probabilistic Model (DDPM).
Input space
TensorNTCHW with shape (batch_size, n_history_steps + n_history_steps * n_era5_channels, height, width) - OSISAF input: T historical steps, singleton channel squeezed - ERA5 input: T historical steps times number of channels, resized to OSISAF resolution
Output space
TensorNTCHW with shape (batch_size, n_forecast_steps * n_output_channels, height, width) - Forecasted outputs per timestep and channel, flattened along the channel dimension
ignored_hparams = BaseModel.ignored_hparams | {'mask_dir'}
class-attribute
¶
use_autoregressive = use_autoregressive
instance-attribute
¶
osisaf_key = self.output_space.name
instance-attribute
¶
restrict = RestrictRange(RangeRestriction(restrict_range), min_val=0, max_val=1)
instance-attribute
¶
mask = Mask(mask_type=mask_type, output_shape=self.output_space.shape, mask_dir=mask_dir)
instance-attribute
¶
era5_space = next(space for space in self.input_spaces if space.name == 'era5').channels
instance-attribute
¶
osisaf_channels = next(space for space in self.input_spaces if space.name == self.osisaf_key).channels
instance-attribute
¶
base_output_channels = self.output_space.channels
instance-attribute
¶
output_channels = self.base_output_channels
instance-attribute
¶
timesteps = timesteps
instance-attribute
¶
cond_channels = 64
instance-attribute
¶
input_channels = self.cond_channels
instance-attribute
¶
era5_norm = torch.nn.InstanceNorm3d(self.era5_space, affine=True)
instance-attribute
¶
era5_compressed_channels = 32
instance-attribute
¶
era5_projector = torch.nn.Sequential(torch.nn.Conv3d(self.era5_space, self.era5_compressed_channels, kernel_size=1), torch.nn.SiLU())
instance-attribute
¶
osisaf_encoder = SimpleEncoder2D(in_channels=self.n_history_steps * self.osisaf_channels, out_channels=self.cond_channels // 2)
instance-attribute
¶
era5_encoder = torch.nn.Sequential(torch.nn.Conv2d(in_channels=self.era5_compressed_channels * self.n_history_steps, out_channels=self.cond_channels // 2, kernel_size=3, padding=1), torch.nn.GroupNorm(4, self.cond_channels // 2), torch.nn.SiLU())
instance-attribute
¶
model = UNetDiffusion(input_channels=self.input_channels, output_channels=self.output_channels, timesteps=self.timesteps, kernel_size=kernel_size, start_out_channels=start_out_channels, time_embed_dim=time_embed_dim, normalization=normalization, activation=activation, dropout_rate=dropout_rate)
instance-attribute
¶
diffusion = GaussianDiffusion(timesteps=timesteps)
instance-attribute
¶
learning_rate = learning_rate
instance-attribute
¶
forward(*args, **kwargs)
¶
sample(batch)
¶
Generate forecasts using a reverse diffusion process.
This method selects between two diffusion sampling strategies:
- Non-autoregressive (parallel) sampling:
- The model generates the entire future sequence in a single diffusion process.
-
No temporal dependency exists between forecast steps.
-
Autoregressive sampling:
- Forecast steps are generated sequentially.
- Each step is produced via an independent diffusion process.
- The conditioning tensor is updated after each step to incorporate previously generated outputs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
batch
|
dict[str, TensorNTCHW]
|
Dictionary containing the input data. |
required |
Returns:
| Type | Description |
|---|---|
TensorNCHW
|
torch.Tensor: Forecast tensor of shape: [B, n_forecast_steps * base_output_channels, H, W] The output format is identical in both modes.
|
Notes
- The diffusion process follows v-parameterization.
- Sampling begins from standard Gaussian noise.
prepare_inputs(batch)
¶
Encode OSISAF and ERA5 separately, then concatenate.
ERA5 -> Norm -> Project -> Resize -> Flatten Time -> Encode
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
batch
|
dict[str, TensorNTCHW]
|
Dictionary with osisaf key (e.g. 'osisaf-south') [B, T, C, H, W] 'era5' [B, T, C, H2, W2] |
required |
Returns:
| Type | Description |
|---|---|
TensorNCHW
|
Conditioning tensor [B, cond_channels, H, W] |
training_step(batch, _batch_idx)
¶
One training step using DDPM v-prediction loss.
During training, the clean target (SIC) is corrupted using the forward diffusion process by adding noise at a randomly sampled timestep. The model is trained to predict the corresponding v-target.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
batch
|
dict[str, TensorNTCHW]
|
Dictionary containing: - input tensors (used to prepare conditioning inputs) - "target": groundtruth SIC tensor |
required |
Returns:
| Name | Type | Description |
|---|---|---|
ModelStepOutput |
ModelStepOutput
|
|
ModelStepOutput
|
|
|
ModelStepOutput
|
|
|
ModelStepOutput
|
|
validation_step(batch, _batch_idx)
¶
One validation step using full diffusion sampling.
During validation, samples are generated by starting from noise and iteratively denoising conditioned on the inputs. The final prediction is compared to the groundtruth SIC using the configured evaluation loss.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
batch
|
dict[str, TensorNTCHW]
|
Dictionary containing: - input tensors (used to prepare conditioning inputs) - "target": groundtruth SIC tensor |
required |
Returns:
| Name | Type | Description |
|---|---|---|
ModelStepOutput |
ModelStepOutput
|
|
ModelStepOutput
|
|
|
ModelStepOutput
|
|
|
ModelStepOutput
|
|
test_step(batch, _batch_idx)
¶
One test step using full diffusion sampling and metric evaluation.
During testing, predictions are generated by starting from noise and running the reverse diffusion process conditioned on the inputs. The final reconstructed SIC is compared to the groundtruth target using the configured loss and test metrics.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
batch
|
dict[str, TensorNTCHW]
|
Dictionary containing: - input tensors (used to prepare conditioning inputs) - "target": groundtruth SIC tensor |
required |
Returns:
| Name | Type | Description |
|---|---|---|
ModelStepOutput |
ModelStepOutput
|
|
ModelStepOutput
|
|
|
ModelStepOutput
|
|
|
ModelStepOutput
|
|
EncodeProcessDecode
¶
Bases: BaseModel
Model that encodes to latent space, processes, then decodes back.
ignored_hparams = BaseModel.ignored_hparams | {'decoder', 'encoders', 'mask_dir', 'processor'}
class-attribute
¶
encoders = [hydra.utils.instantiate(encoders[input_space.name], data_space_in=input_space, latent_space=encoders['latent_space'], latitudes_fn=self.latitudes_fn, longitudes_fn=self.longitudes_fn) for input_space in self.input_spaces] if isinstance(encoders, Mapping) else [encoder for input_space in self.input_spaces for encoder in encoders if encoder.name == input_space.name]
instance-attribute
¶
target_encoder = hydra.utils.instantiate(encoders[self.output_space.name], data_space_in=DataSpace(name='target', channels=self.output_space.channels, shape=self.output_space.shape), latent_space=encoders['latent_space'], latitudes_fn=self.latitudes_fn, longitudes_fn=self.longitudes_fn) if isinstance(encoders, Mapping) else encoders.pop(next(idx for idx, encoder in enumerate(encoders) if encoder.name == 'target'))
instance-attribute
¶
processor = processor if isinstance(processor, BaseProcessor) else hydra.utils.instantiate(processor, _recursive_=False, data_space=combined_latent_space, data_space_target=(self.target_encoder if target_input_encoder is None else target_input_encoder).data_space_out, n_forecast_steps=self.n_forecast_steps, n_history_steps=self.n_history_steps, target_channel_offset=self._find_target_channel_offset())
instance-attribute
¶
decoder = decoder if isinstance(decoder, BaseDecoder) else hydra.utils.instantiate(decoder, data_space_in=combined_latent_space, data_space_out=self.output_space, mask_dir=mask_dir)
instance-attribute
¶
rollout_space = self._validate_rollout_options(rollout_space)
instance-attribute
¶
multistage_only
property
¶
target_input_encoder
property
¶
Return the input encoder whose dataset contains the forecast target.
forward(inputs)
¶
Forward step of the model (used for inference).
Delegates to either the latent-space or physical-space path depending on the
rollout_space setting.
train(mode=True)
¶
Set training mode, with decoder frozen if computing loss in latent space.
training_step(batch, _batch_idx)
¶
Run the training step.
The standard path is to call the forward step with self(batch) and compute the
loss by comparing the decoded prediction to the target.
Processors that set the flag computes_loss_in_latent_space=True are a special
case. We encode the inputs and target into latent space, call rollout() on the
processor, which returns both a latent prediction and a loss. We pass this
prediction through a frozen decoder in order to calculate metrics, but we use
the loss returned by the processor for backpropagation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
batch
|
dict[str, TensorNTCHW]
|
Dictionary with one NTCHW entry per input dataset (n_history_steps) and a "target" entry (n_forecast_steps). |
required |
Returns:
| Type | Description |
|---|---|
ModelStepOutput
|
A ModelStepOutput containing the prediction, target and loss. |
Persistence
¶
Bases: BaseModel
automatic_optimization = False
instance-attribute
¶
model = nn.Identity()
instance-attribute
¶
configure_optimizers()
¶
Persistence model does not need an optimizer.
forward(inputs)
¶
Forward step of the model.
- start with multiple
NTCHWinputs each with shape (batch, n_history_steps, C_input_k, H_input_k, W_input_k) - find the input with the same name as the output space
- select the channels corresponding to the target variables
- take the last time step and repeat it n_forecast_steps times