Nowcasting Tutorial#
In this tutorial, we will show how to use Karman to nowcast thermospheric density. In essence, we do the same as an empirical model (e.g. NRLMSISE-00 or JB-08), but using a lightweight ML model (very small deep network with a few thousands of parameters).
Imports#
import sys
sys.path.append('../../')
import karman
import pandas as pd
import torch
import datetime
import numpy as np
import pickle as pk
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
torch.set_default_dtype(torch.float32)
Data Preparation#
We generate the lat/lon grid:
n_grid=100
u, v = np.linspace(0, 1, n_grid), np.linspace(0, 1, n_grid)
longitude, latitude = np.sort(2 * np.pi * u), np.sort(np.arccos(2 * v - 1) - np.pi / 2)
lonlat_grid = np.stack([*np.meshgrid(longitude, latitude, indexing="ij")], axis=2).reshape(-1, 2)
lonlat_grid[:,0]-=np.pi
and now the altitude and dates:
#let's prepare the inputs to the model and run it:
longitudes=list(np.rad2deg(lonlat_grid[:,0],dtype=np.float32))
latitudes=list(np.rad2deg(lonlat_grid[:,1],dtype=np.float32))
n_samples=len(longitudes)
altitudes=[450000.]*n_samples#250km in meters
dates=['2024-05-08 00:59:47']*n_samples
Small Deep Neural Network Nowcasting Model#
Here we load the 3x128 feed forward network
#input features to the model
num_features=18
nowcasting_model=karman.density_models.NowcastingModel(num_instantaneous_features=18,
hidden_layer_dims=128,
hidden_layers=3)
nowcasting_model.load_model(model_path='../../models/karman_nowcast_model_log_exp_residual_valid_mape_15.14_params_35585.torch')
Loaded model with 35585 parameters
/home/ga00693/karman/karman/density_models.py:216: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature.
karman_model.load_state_dict(torch.load(model_path))
density_nn = nowcasting_model(dates=dates,
altitudes=altitudes,
longitudes=longitudes,
latitudes=latitudes,
device=torch.device('cpu'))
NRLMSISE-00 Data#
We also run NRLMSISE-00, for comparison. Keep in mind that the ML models are not trained with this, but directly with precise-orbit determination-derived satellites data
#let's extract the SW inputs to compute NRLMSISE-00:
sw_df=karman.util.find_sw_from_thermo(pd.to_datetime(dates),karman.density_models.df_sw)
ap=sw_df['celestrack__ap_average__'].values
f107=sw_df['space_environment_technologies__f107_obs__'].values
f107A=sw_df['space_environment_technologies__f107_average__'].values
from nrlmsise00 import msise_flat
d=pd.to_datetime(dates)
density_nrlmsise00=msise_flat(d.to_pydatetime(), np.array(altitudes)*1e-3, latitudes, longitudes, f107A, f107, ap)[:,5]*1e3
# we setup the longitude x latitude grid, and compute the relative error (in %)
lon_grid = np.rad2deg(lonlat_grid[:, 0].reshape((n_grid, n_grid)))
lat_grid = np.rad2deg(lonlat_grid[:, 1].reshape((n_grid, n_grid)))
# we now create a figure with a globe projection on top:
fig, ax = plt.subplots(
figsize=(18, 12),
nrows=1,
ncols=2,
subplot_kw={"projection": ccrs.Mollweide(central_longitude=0)},
)
# we flatten the axis and remove the last figure
#ax = ax.ravel()
ax[0].axis("off")
# we plot NRLMSISE-00 on the first figure:
ax[0].pcolormesh(
lon_grid,
lat_grid,
density_nrlmsise00.reshape((n_grid, n_grid)),
transform=ccrs.PlateCarree(),
vmin=min(density_nrlmsise00),
vmax=max(density_nrlmsise00),
)
ax[0].set_global()
ax[0].coastlines()
# ax[0].gridlines()
ax[0].set_title("NRLMSISE-00 Prediction")
# the NN prediction on the second:
im2 = ax[1].pcolormesh(
lon_grid,
lat_grid,
density_nn.reshape((n_grid, n_grid)),
transform=ccrs.PlateCarree(),
vmin=min(density_nn),
vmax=max(density_nn),
)
ax[1].set_global()
ax[1].coastlines()
# ax[1].gridlines()
ax[1].set_title("Karman Nowcasting Prediction")
plt.show()