Multi-Mission MAG#
Abstract: An introduction to “the greater Swarm”: calibrated platform magnetometer data from other missions. We begin with Cryosat-2, GRACE, GRACE-FO, and GOCE
See also:
SERVER_URL = "https://vires.services/ows"
%load_ext watermark
%watermark -i -v -p viresclient,pandas,xarray,matplotlib,cartopy
Python implementation: CPython
Python version : 3.11.6
IPython version : 8.18.0
viresclient: 0.12.0
pandas : 2.1.3
xarray : 2023.12.0
matplotlib : 3.8.2
cartopy : 0.22.0
from viresclient import SwarmRequest
import datetime as dt
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from tqdm.notebook import tqdm
Product information#
Platform magnetometer data from other missions have been carefully recalibrated so that they are accurate and suitable for usage in geomagnetic field modelling. The data currently available from VirES are as follows:
CryoSat-2:
Olsen, N., Albini, G., Bouffard, J. et al., Magnetic observations from CryoSat-2: calibration and processing of satellite platform magnetometer data. Earth Planets Space 72, 48 (2020).
https://doi.org/10.1186/s40623-020-01171-9
VirES collection names:"CS_OPER_MAG"
GRACE (x2):
Olsen, N., Magnetometer data from the GRACE satellite duo. Earth Planets Space 73, 62 (2021).
https://doi.org/10.1186/s40623-021-01373-9
VirES collection names:"GRACE_A_MAG"
,"GRACE_B_MAG"
GRACE-FO (x2):
Stolle, C., Michaelis, I., Xiong, C. et al., Observing Earth’s magnetic environment with the GRACE-FO mission. Earth Planets Space 73, 51 (2021).
https://doi.org/10.1186/s40623-021-01364-w
VirES collection names:"GF1_OPER_FGM_ACAL_CORR"
,"GF2_OPER_FGM_ACAL_CORR"
GRACE-FO (x2) (ML-calibrated):
Styp-Rekowski, Kevin; Michaelis, Ingo; Korte, Monika; Stolle, Claudia (2023): GRACE-FO ML-calibrated magnetic field data. V. 0302. GFZ Data Services.
https://doi.org/10.5880/GFZ.2.3.2023.001
VirES collection names:"GF1_MAG_ACAL_CORR_ML"
,"GF2_MAG_ACAL_CORR_ML"
GOCE:
Michaelis, Ingo; Korte, Monika (2022), GOCE calibrated and characterised magnetometer data. V. 0205. GFZ Data Services.
https://doi.org/10.5880/GFZ.2.3.2022.001
VirES collection names:"GO_MAG_ACAL_CORR"
GOCE (ML-calibrated):
Styp-Rekowski, Kevin; Michaelis, Ingo; Stolle, Claudia; Baerenzung, Julien; Korte, Monika; Kao, Odej (2022): GOCE ML-calibrated magnetic field data. V. 0204. GFZ Data Services.
https://doi.org/10.5880/GFZ.2.3.2022.002
VirES collection names:"GO_MAG_ACAL_CORR_ML"
In addition to those data, we also have the full magnetic product from CHAMP:
CHAMP:
Rother, Martin; Michaelis, Ingo (2019): CH-ME-3-MAG - CHAMP 1 Hz Combined Magnetic Field Time Series (Level 3). GFZ Data Services. https://doi.org/10.5880/GFZ.2.3.2019.004
VirES collection name:"CH_ME_MAG_LR_3"
The variables available from each collection are:
request = SwarmRequest(SERVER_URL)
for collection in ("CH_ME_MAG_LR_3", "CS_OPER_MAG", "GRACE_A_MAG", "GF1_OPER_FGM_ACAL_CORR", "GF1_MAG_ACAL_CORR_ML", "GO_MAG_ACAL_CORR", "GO_MAG_ACAL_CORR_ML"):
print(f"{collection}:\n{request.available_measurements(collection)}\n")
CH_ME_MAG_LR_3:
['F', 'B_VFM', 'B_NEC', 'Flags_Position', 'Flags_B', 'Flags_q', 'Mode_q', 'q_ICRF_CRF']
CS_OPER_MAG:
['F', 'B_NEC', 'B_mod_NEC', 'B_NEC1', 'B_NEC2', 'B_NEC3', 'B_FGM1', 'B_FGM2', 'B_FGM3', 'q_NEC_CRF', 'q_error']
GRACE_A_MAG:
['F', 'B_NEC', 'B_NEC_raw', 'B_FGM', 'q_NEC_CRF', 'q_error']
GF1_OPER_FGM_ACAL_CORR:
['F', 'B_NEC', 'B_FGM', 'dB_MTQ_FGM', 'dB_XI_FGM', 'dB_NY_FGM', 'dB_BT_FGM', 'dB_ST_FGM', 'dB_SA_FGM', 'dB_BAT_FGM', 'q_NEC_FGM', 'B_FLAG']
GF1_MAG_ACAL_CORR_ML:
['F', 'B_MAG', 'B_NEC', 'q_NEC_FGM', 'B_FLAG', 'KP_DST_FLAG', 'Latitude_QD', 'Longitude_QD']
GO_MAG_ACAL_CORR:
['F', 'B_MAG', 'B_NEC', 'dB_MTQ_SC', 'dB_XI_SC', 'dB_NY_SC', 'dB_BT_SC', 'dB_ST_SC', 'dB_SA_SC', 'dB_BAT_SC', 'dB_HK_SC', 'dB_BLOCK_CORR', 'q_SC_NEC', 'q_MAG_SC', 'B_FLAG']
GO_MAG_ACAL_CORR_ML:
['F', 'B_MAG', 'B_NEC', 'q_FGM_NEC', 'B_FLAG', 'MAGNETIC_ACTIVITY_FLAG', 'NaN_FLAG', 'Latitude_QD', 'Longitude_QD']
Where additional B_NEC
variables are specified (B_NEC1
, B_NEC2
, B_NEC3
), these correspond to measurements from separate magnetometers on-board the spacecraft. See the scientific publications for details. Magnetic model evaluation will also work with those variables.
The temporal availabilities of data are:
availabilities = {}
for collection in (
"SW_OPER_MAGA_LR_1B", "SW_OPER_MAGB_LR_1B", "SW_OPER_MAGC_LR_1B",
"CH_ME_MAG_LR_3",
"CS_OPER_MAG",
"GRACE_A_MAG", "GRACE_B_MAG",
"GF1_OPER_FGM_ACAL_CORR", "GF2_OPER_FGM_ACAL_CORR",
"GF1_MAG_ACAL_CORR_ML", "GF2_MAG_ACAL_CORR_ML",
"GO_MAG_ACAL_CORR", "GO_MAG_ACAL_CORR_ML",
):
df = request.available_times(collection)
start = df["starttime"].iloc[0].to_pydatetime()
end = df["endtime"].iloc[-1].to_pydatetime()
availabilities[collection] = (start, end)
for collection, (start, end) in availabilities.items():
print(f"{collection}:\n {start.isoformat()} to {end.isoformat()}")
SW_OPER_MAGA_LR_1B:
2013-11-25T11:02:52+00:00 to 2024-09-16T23:59:59.001000+00:00
SW_OPER_MAGB_LR_1B:
2013-11-25T11:01:15+00:00 to 2024-09-16T23:59:59.001000+00:00
SW_OPER_MAGC_LR_1B:
2013-11-25T11:02:17+00:00 to 2024-09-16T23:59:59.001000+00:00
CH_ME_MAG_LR_3:
2000-07-19T00:00:00.140000+00:00 to 2010-09-17T20:16:28.141000+00:00
CS_OPER_MAG:
2010-04-10T21:55:26.241000+00:00 to 2024-07-31T23:10:40.895000+00:00
GRACE_A_MAG:
2008-01-01T00:00:04.787680+00:00 to 2017-10-31T23:59:38.946023+00:00
GRACE_B_MAG:
2008-01-01T00:00:04.971828+00:00 to 2017-09-04T15:11:05.000953+00:00
GF1_OPER_FGM_ACAL_CORR:
2018-06-01T00:00:00+00:00 to 2024-07-31T23:59:59.001000+00:00
GF2_OPER_FGM_ACAL_CORR:
2018-06-01T00:00:00+00:00 to 2024-07-31T23:59:59.001000+00:00
GF1_MAG_ACAL_CORR_ML:
2018-06-01T00:00:00+00:00 to 2023-07-31T23:59:59.001000+00:00
GF2_MAG_ACAL_CORR_ML:
2018-06-01T00:00:00+00:00 to 2023-07-31T23:59:59.001000+00:00
GO_MAG_ACAL_CORR:
2009-11-01T00:49:15.411000+00:00 to 2013-09-30T02:47:35.587000+00:00
GO_MAG_ACAL_CORR_ML:
2009-11-01T00:49:15.411000+00:00 to 2013-09-30T02:47:35.587000+00:00
fig, ax = plt.subplots(1, 1)
for i, (collection, (start, end)) in enumerate(availabilities.items()):
ax.plot([start, end], [-i, -i], label=collection)
ax.legend(bbox_to_anchor=(1, 1, 0, 0))
ax.set_yticks([])
ax.grid()
Access works just like Swarm MAG products#
We can specify which collection to fetch, and which models to evaluate at the same time:
request = SwarmRequest()
request.set_collection("GF1_OPER_FGM_ACAL_CORR")
request.set_products(["B_NEC"], models=["IGRF"])
data = request.get_between("2018-06-01", "2018-06-02")
ds = data.as_xarray()
ds
<xarray.Dataset> Dimensions: (Timestamp: 86400, NEC: 3) Coordinates: * Timestamp (Timestamp) datetime64[ns] 2018-06-01 ... 2018-06-01T23:59:59 * NEC (NEC) <U1 'N' 'E' 'C' Data variables: Spacecraft (Timestamp) object '1' '1' '1' '1' '1' ... '1' '1' '1' '1' '1' B_NEC (Timestamp, NEC) float64 2.527e+04 -2.319e+03 ... 4.648e+04 Latitude (Timestamp) float64 15.74 15.8 15.86 15.93 ... 83.02 82.95 82.89 B_NEC_IGRF (Timestamp, NEC) float64 2.528e+04 -2.316e+03 ... 4.647e+04 Radius (Timestamp) float64 6.881e+06 6.881e+06 ... 6.873e+06 6.873e+06 Longitude (Timestamp) float64 -11.27 -11.27 -11.27 ... 159.1 159.2 159.3 Attributes: Sources: ['GF1_OPER_FGM_ACAL_CORR_20180601T000000_20180601T235959... MagneticModels: ['IGRF = IGRF(max_degree=13,min_degree=1)'] AppliedFilters: []
# Append the residual, B - IGRF
ds["B_NEC_res_IGRF"] = ds["B_NEC"] - ds["B_NEC_IGRF"]
# Plot (B) and (B - IGRF) to compare
fig, axes = plt.subplots(nrows=2, ncols=1, figsize=(10, 5), sharex=True)
ds["B_NEC"].plot.line(x="Timestamp", ax=axes[0])
ds["B_NEC_res_IGRF"].plot.line(x="Timestamp", ax=axes[1]);
Data from multiple spacecraft#
Here is an example of fetching and visualising data from multiple spacecraft. We select a day where we can get data from Swarm, CryoSat, and GRACE-FO.
START = dt.datetime(2018, 6, 1)
END = dt.datetime(2018, 6, 2)
# Mappings to identify spacecraft and collection names
# Let's disable Swarm Charlie & GRACE-FO 2 for now
# as they are in similar places as Swarm Alpha and GRACE-FO 1
spacecraft_to_collections = {
"Swarm Alpha": "SW_OPER_MAGA_LR_1B",
"Swarm Bravo": "SW_OPER_MAGB_LR_1B",
# "Swarm Charlie": "SW_OPER_MAGC_LR_1B",
"CryoSat-2": "CS_OPER_MAG",
"GRACE-FO 1": "GF1_OPER_FGM_ACAL_CORR",
# "GRACE-FO 2": "GF2_OPER_FGM_ACAL_CORR"
}
collections_to_spacecraft = {v: k for k, v in spacecraft_to_collections.items()}
def fetch_sc(sc_collection, start_time=START, end_time=END, **kwargs):
"""Fetch data from a specific spacecraft"""
request = SwarmRequest(SERVER_URL)
request.set_collection(sc_collection)
request.set_products(["B_NEC"])
data = request.get_between(start_time, end_time, **kwargs)
ds = data.as_xarray()
# Rename the Spacecraft variable to use the mission name too
ds.Spacecraft[:] = collections_to_spacecraft[sc_collection]
return ds
ds_set = {}
for sc in tqdm(spacecraft_to_collections.keys()):
collection = spacecraft_to_collections[sc]
ds_set[sc] = fetch_sc(collection, asynchronous=False, show_progress=False)
Data are now stored within datasets within a dictionary:
ds_set["Swarm Alpha"]
<xarray.Dataset> Dimensions: (Timestamp: 86400, NEC: 3) Coordinates: * Timestamp (Timestamp) datetime64[ns] 2018-06-01 ... 2018-06-01T23:59:59 * NEC (NEC) <U1 'N' 'E' 'C' Data variables: Spacecraft (Timestamp) object 'Swarm Alpha' 'Swarm Alpha' ... 'Swarm Alpha' B_NEC (Timestamp, NEC) float64 1.488e+04 -5.48e+03 ... -2.469e+04 Latitude (Timestamp) float64 -13.68 -13.74 -13.81 ... -15.43 -15.37 -15.3 Longitude (Timestamp) float64 -25.48 -25.48 -25.48 ... 151.8 151.8 151.8 Radius (Timestamp) float64 6.82e+06 6.82e+06 ... 6.82e+06 6.82e+06 Attributes: Sources: ['SW_OPER_MAGA_LR_1B_20180601T000000_20180601T235959_060... MagneticModels: [] AppliedFilters: []
A quick inspection of the data:
ds_set["Swarm Alpha"].plot.scatter(x="Longitude", y="Latitude", hue="B_NEC", s=1, linewidths=0);
fig, axes = plt.subplots(
nrows=2, figsize=(15,15),
subplot_kw={"projection": ccrs.PlateCarree()}
)
for ax in axes:
ax.add_feature(cfeature.COASTLINE, edgecolor='silver')
for sc in ("Swarm Alpha", "Swarm Bravo", "CryoSat-2", "GRACE-FO 1"):
# Extract from dataset and plot contents
_ds = ds_set[sc]
lon, lat = _ds["Longitude"], _ds["Latitude"]
B_C = _ds["B_NEC"].sel(NEC="C").values
# Plot positions coloured by spacecraft
axes[0].scatter(x=lon, y=lat, s=0.1, label=sc)
# Plot vertical magnetic field
norm = plt.Normalize(vmin=-60000, vmax=60000)
cmap = "viridis"
axes[1].scatter(x=lon, y=lat, c=B_C, s=0.1, norm=norm, cmap=cmap)
fig.colorbar(plt.cm.ScalarMappable(norm=norm, cmap=cmap), ax=axes[1], label=r"$B_C$ [nT]");
axes[0].legend(loc="upper right", markerscale=20)
axes[0].set_title("Orbits from each spacecraft")
axes[1].set_title("Vertical component magnetic measurements");