MAGxLR_1B (Magnetic field 1Hz)#

Abstract: Access to the low rate (1Hz) magnetic data (level 1b product), together with geomagnetic model evaluations (level 2 products).

%load_ext watermark
%watermark -i -v -p viresclient,pandas,xarray,matplotlib
Python implementation: CPython
Python version       : 3.11.6
IPython version      : 8.18.0

viresclient: 0.12.3
pandas     : 2.1.3
xarray     : 2023.12.0
matplotlib : 3.8.2
from viresclient import SwarmRequest
import datetime as dt
import matplotlib.pyplot as plt

request = SwarmRequest()

Product information#

This is one of the main products from Swarm - the 1Hz measurements of the magnetic field vector (B_NEC) and total intensity (F). These are derived from the Vector Field Magnetometer (VFM) and Absolute Scalar Magnetomer (ASM).

Documentation:

Measurements are available through VirES as part of collections with names containing MAGx_LR, for each Swarm spacecraft:

request.available_collections("MAG", details=False)
{'MAG': ['SW_OPER_MAGA_LR_1B',
  'SW_OPER_MAGB_LR_1B',
  'SW_OPER_MAGC_LR_1B',
  'SW_FAST_MAGA_LR_1B',
  'SW_FAST_MAGB_LR_1B',
  'SW_FAST_MAGC_LR_1B']}

The measurements can be used together with geomagnetic model evaluations as shall be shown below.

Check what “MAG” data variables are available#

request.available_measurements("MAG")
['F',
 'dF_Sun',
 'dF_AOCS',
 'dF_other',
 'F_error',
 'B_VFM',
 'B_NEC',
 'dB_Sun',
 'dB_AOCS',
 'dB_other',
 'B_error',
 'q_NEC_CRF',
 'Att_error',
 'Flags_F',
 'Flags_B',
 'Flags_q',
 'Flags_Platform',
 'ASM_Freq_Dev']

Check the names of available models#

request.available_models(details=False)
['IGRF',
 'LCS-1',
 'MF7',
 'CHAOS-Core',
 'CHAOS-Static',
 'CHAOS-MMA-Primary',
 'CHAOS-MMA-Secondary',
 'MCO_SHA_2C',
 'MCO_SHA_2D',
 'MLI_SHA_2C',
 'MLI_SHA_2D',
 'MLI_SHA_2E',
 'MMA_SHA_2C-Primary',
 'MMA_SHA_2C-Secondary',
 'MMA_SHA_2F-Primary',
 'MMA_SHA_2F-Secondary',
 'MIO_SHA_2C-Primary',
 'MIO_SHA_2C-Secondary',
 'MIO_SHA_2D-Primary',
 'MIO_SHA_2D-Secondary',
 'AMPS',
 'MCO_SHA_2X',
 'CHAOS',
 'CHAOS-MMA',
 'MMA_SHA_2C',
 'MMA_SHA_2F',
 'MIO_SHA_2C',
 'MIO_SHA_2D',
 'SwarmCI']

Fetch some MAG data and models#

We can fetch the data and the model predictions (evaluated on demand) at the same time. We can also subsample the data - here we subsample it to 10-seconds by specifying the “PT10S” sampling_step.

request.set_collection("SW_OPER_MAGA_LR_1B")
request.set_products(
    measurements=["F", "B_NEC"],
    models=["CHAOS-Core", "MCO_SHA_2D"],
    sampling_step="PT10S"
)
data = request.get_between(
    # 2014-01-01 00:00:00
    start_time = dt.datetime(2014,1,1, 0),
    # 2014-01-01 01:00:00
    end_time = dt.datetime(2014,1,1, 1)
)
Processing: 100%
[ Elapsed: 00:01, Remaining: 00:00 ] [1/1]
Downloading: 100%
[ Elapsed: 00:00, Remaining: 00:00 ] (0.098MB)

See a list of the source files#

data.sources
['SW_OPER_MAGA_LR_1B_20140101T000000_20140101T235959_0602_MDR_MAG_LR',
 'SW_OPER_MCO_SHA_2D_20131126T000000_20180101T000000_0401',
 'SW_OPER_MCO_SHA_2X_19970101T000000_20250206T235959_0802']

Load as a pandas dataframe#

Use expand=True to extract vectors (B_NEC…) as separate columns (…_N, …_E, …_C)

df = data.as_dataframe(expand=True)
df.head()
F_MCO_SHA_2D Radius Latitude F Longitude F_CHAOS-Core Spacecraft B_NEC_CHAOS-Core_N B_NEC_CHAOS-Core_E B_NEC_CHAOS-Core_C B_NEC_N B_NEC_E B_NEC_C B_NEC_MCO_SHA_2D_N B_NEC_MCO_SHA_2D_E B_NEC_MCO_SHA_2D_C
Timestamp
2014-01-01 00:00:00 22874.210752 6878309.29 -1.228937 22867.4381 -14.116675 22874.187462 A 20113.447623 -4127.341112 -10081.802026 20103.5295 -4126.0959 -10086.8832 20113.623744 -4127.463817 -10081.453262
2014-01-01 00:00:10 22820.940668 6878381.24 -1.862520 22814.4502 -14.131425 22820.917147 A 19824.979932 -4162.995604 -10508.753385 19815.0632 -4160.8621 -10514.3566 19825.161689 -4163.127409 -10508.409355
2014-01-01 00:00:20 22769.368411 6878452.12 -2.496089 22763.1399 -14.146156 22769.346642 A 19533.368312 -4197.388760 -10921.199407 19523.4409 -4195.1070 -10926.9487 19533.553819 -4197.528908 -10920.859129
2014-01-01 00:00:30 22719.237483 6878521.94 -3.129643 22713.2484 -14.160861 22719.219291 A 19239.155618 -4230.453824 -11319.058120 19229.2127 -4228.3665 -11324.7764 19239.343461 -4230.601678 -11318.720089
2014-01-01 00:00:40 22670.303932 6878590.68 -3.763183 22664.5952 -14.175535 22670.290885 A 18942.885772 -4262.125422 -11702.284151 18932.8424 -4260.6440 -11708.0879 18943.075099 -4262.280487 -11701.946471

… or as an xarray dataset:#

ds = data.as_xarray()
ds
<xarray.Dataset>
Dimensions:           (Timestamp: 360, NEC: 3)
Coordinates:
  * Timestamp         (Timestamp) datetime64[ns] 2014-01-01 ... 2014-01-01T00...
  * NEC               (NEC) <U1 'N' 'E' 'C'
Data variables:
    Spacecraft        (Timestamp) object 'A' 'A' 'A' 'A' 'A' ... 'A' 'A' 'A' 'A'
    B_NEC_CHAOS-Core  (Timestamp, NEC) float64 2.011e+04 ... 3.558e+04
    F_MCO_SHA_2D      (Timestamp) float64 2.287e+04 2.282e+04 ... 4.021e+04
    Radius            (Timestamp) float64 6.878e+06 6.878e+06 ... 6.868e+06
    Latitude          (Timestamp) float64 -1.229 -1.863 -2.496 ... 48.14 48.77
    F                 (Timestamp) float64 2.287e+04 2.281e+04 ... 4.021e+04
    Longitude         (Timestamp) float64 -14.12 -14.13 -14.15 ... 153.6 153.6
    B_NEC             (Timestamp, NEC) float64 2.01e+04 -4.126e+03 ... 3.558e+04
    F_CHAOS-Core      (Timestamp) float64 2.287e+04 2.282e+04 ... 4.02e+04
    B_NEC_MCO_SHA_2D  (Timestamp, NEC) float64 2.011e+04 ... 3.557e+04
Attributes:
    Sources:         ['SW_OPER_MAGA_LR_1B_20140101T000000_20140101T235959_060...
    MagneticModels:  ["CHAOS-Core = 'CHAOS-Core'(max_degree=20,min_degree=1)"...
    AppliedFilters:  []

Fetch the residuals directly#

Adding residuals=True to .set_products() will instead directly evaluate and return all data-model residuals

request = SwarmRequest()
request.set_collection("SW_OPER_MAGA_LR_1B")
request.set_products(
    measurements=["F", "B_NEC"],
    models=["CHAOS-Core", "MCO_SHA_2D"],
    residuals=True,
    sampling_step="PT10S"
)
data = request.get_between(
    start_time = dt.datetime(2014,1,1, 0),
    end_time = dt.datetime(2014,1,1, 1)
)
df = data.as_dataframe(expand=True)
df.head()
Processing: 100%
[ Elapsed: 00:01, Remaining: 00:00 ] [1/1]
Downloading: 100%
[ Elapsed: 00:00, Remaining: 00:00 ] (0.081MB)
F_res_MCO_SHA_2D Radius Latitude Longitude Spacecraft F_res_CHAOS-Core B_NEC_res_MCO_SHA_2D_N B_NEC_res_MCO_SHA_2D_E B_NEC_res_MCO_SHA_2D_C B_NEC_res_CHAOS-Core_N B_NEC_res_CHAOS-Core_E B_NEC_res_CHAOS-Core_C
Timestamp
2014-01-01 00:00:00 -6.772652 6878309.29 -1.228937 -14.116675 A -6.749362 -10.094244 1.367917 -5.429938 -9.918123 1.245212 -5.081174
2014-01-01 00:00:10 -6.490468 6878381.24 -1.862520 -14.131425 A -6.466947 -10.098489 2.265309 -5.947245 -9.916732 2.133504 -5.603215
2014-01-01 00:00:20 -6.228511 6878452.12 -2.496089 -14.146156 A -6.206742 -10.112919 2.421908 -6.089571 -9.927412 2.281760 -5.749293
2014-01-01 00:00:30 -5.989083 6878521.94 -3.129643 -14.160861 A -5.970891 -10.130761 2.235178 -6.056311 -9.942918 2.087324 -5.718280
2014-01-01 00:00:40 -5.708732 6878590.68 -3.763183 -14.175535 A -5.695685 -10.232699 1.636487 -6.141429 -10.043372 1.481422 -5.803749

Plot the scalar residuals#

… using the pandas method:#

ax = df.plot(
    y=["F_res_CHAOS-Core", "F_res_MCO_SHA_2D"],
    figsize=(15,5),
    grid=True
)
ax.set_xlabel("Timestamp")
ax.set_ylabel("[nT]");
../_images/2243a70ce57de87dd992ebb9b45445d383b9f76a2b0df30a571fde4f91301a58.png

… using matplotlib interface#

NB: we are doing plt.plot(x, y) with x as df.index (the time-based index of df), and y as df[".."]

plt.figure(figsize=(15,5))
plt.plot(
    df.index,
    df["F_res_CHAOS-Core"],
    label="F_res_CHAOS-Core"
)
plt.plot(
    df.index,
    df["F_res_MCO_SHA_2D"],
    label="F_res_MCO_SHA_2D"
)
plt.xlabel("Timestamp")
plt.ylabel("[nT]")
plt.grid()
plt.legend();
../_images/2d84d24776ea21130b0d88dd33910907625c12a3709f84000a46e51fc9e03e6d.png

… using matplotlib interface (Object Oriented style)#

This is the recommended route for making more complicated figures

fig, ax = plt.subplots(figsize=(15,5))
ax.plot(
    df.index,
    df["F_res_CHAOS-Core"],
    label="F_res_CHAOS-Core"
)
ax.plot(
    df.index,
    df["F_res_MCO_SHA_2D"],
    label="F_res_MCO_SHA_2D"
)
ax.set_xlabel("Timestamp")
ax.set_ylabel("[nT]")
ax.grid()
ax.legend();
../_images/2d84d24776ea21130b0d88dd33910907625c12a3709f84000a46e51fc9e03e6d.png

Plot the vector components#

fig, axes = plt.subplots(nrows=3, ncols=1, figsize=(15,10), sharex=True)
for component, ax in zip("NEC", axes):
    for model_name in ("CHAOS-Core", "MCO_SHA_2D"):
        ax.plot(
            df.index,
            df[f"B_NEC_res_{model_name}_{component}"],
            label=model_name
        )
    ax.set_ylabel(f"{component}\n[nT]")
    ax.legend()
axes[0].set_title("Residuals to models (NEC components)")
axes[2].set_xlabel("Timestamp");
../_images/2c04a286b3113ce048aae983c1967f16f37964bb98f116e7026113e8f4a1865a.png

Similar plotting, using the data via xarray instead#

xarray provides a more sophisticated data structure that is more suitable for the complex vector data we are accessing, together with nice stuff like unit and other metadata support. Unfortunately due to the extra complexity, this can make it difficult to use right away.

ds = data.as_xarray()
ds
<xarray.Dataset>
Dimensions:               (Timestamp: 360, NEC: 3)
Coordinates:
  * Timestamp             (Timestamp) datetime64[ns] 2014-01-01 ... 2014-01-0...
  * NEC                   (NEC) <U1 'N' 'E' 'C'
Data variables:
    Spacecraft            (Timestamp) object 'A' 'A' 'A' 'A' ... 'A' 'A' 'A' 'A'
    B_NEC_res_MCO_SHA_2D  (Timestamp, NEC) float64 -10.09 1.368 ... 3.074 9.198
    F_res_MCO_SHA_2D      (Timestamp) float64 -6.773 -6.49 ... 3.122 3.077
    Radius                (Timestamp) float64 6.878e+06 6.878e+06 ... 6.868e+06
    Latitude              (Timestamp) float64 -1.229 -1.863 ... 48.14 48.77
    Longitude             (Timestamp) float64 -14.12 -14.13 ... 153.6 153.6
    B_NEC_res_CHAOS-Core  (Timestamp, NEC) float64 -9.918 1.245 ... 3.224 8.788
    F_res_CHAOS-Core      (Timestamp) float64 -6.749 -6.467 ... 3.699 3.681
Attributes:
    Sources:         ['SW_OPER_MAGA_LR_1B_20140101T000000_20140101T235959_060...
    MagneticModels:  ["CHAOS-Core = 'CHAOS-Core'(max_degree=20,min_degree=1)"...
    AppliedFilters:  []
fig, axes = plt.subplots(nrows=3, ncols=1, figsize=(15,10), sharex=True)
for i, ax in enumerate(axes):
    for model_name in ("CHAOS-Core", "MCO_SHA_2D"):
        ax.plot(
            ds["Timestamp"],
            ds[f"B_NEC_res_{model_name}"][:, i],
            label=model_name
        )
    ax.set_ylabel("NEC"[i] + " [nT]")
    ax.legend()
axes[0].set_title("Residuals to models (NEC components)")
axes[2].set_xlabel("Timestamp");
../_images/804f750ca797a13a579c139c1f326ab3dd1b14570c73f06981fd13219cb80024.png

Note that xarray also allows convenient direct plotting like:

ds["B_NEC_res_CHAOS-Core"].plot.line(x="Timestamp");
../_images/94dcb95091fb794b07360b6487cecac59720f086c3b3d27cb3566e9e7476e242.png

Access multiple MAG datasets simultaneously#

It is possible to fetch data from multiple collections simultaneously. Here we fetch the measurements from Swarm Alpha and Bravo. In the returned data, you can differentiate between them using the “Spacecraft” column.

request = SwarmRequest()
request.set_collection("SW_OPER_MAGA_LR_1B", "SW_OPER_MAGC_LR_1B")
request.set_products(
    measurements=["F", "B_NEC"],
    models=["CHAOS-Core",],
    residuals=True,
    sampling_step="PT10S"
)
data = request.get_between(
    start_time = dt.datetime(2014,1,1, 0),
    end_time = dt.datetime(2014,1,1, 1)
)
df = data.as_dataframe(expand=True)
df.head()
Processing: 100%
[ Elapsed: 00:01, Remaining: 00:00 ] [1/1]
Downloading: 100%
[ Elapsed: 00:00, Remaining: 00:00 ] (0.072MB)
Radius Latitude Longitude Spacecraft F_res_CHAOS-Core B_NEC_res_CHAOS-Core_N B_NEC_res_CHAOS-Core_E B_NEC_res_CHAOS-Core_C
Timestamp
2014-01-01 00:00:00 6878309.29 -1.228937 -14.116675 A -6.749362 -9.918123 1.245212 -5.081174
2014-01-01 00:00:10 6878381.24 -1.862520 -14.131425 A -6.466947 -9.916732 2.133504 -5.603215
2014-01-01 00:00:20 6878452.12 -2.496089 -14.146156 A -6.206742 -9.927412 2.281760 -5.749293
2014-01-01 00:00:30 6878521.94 -3.129643 -14.160861 A -5.970891 -9.942918 2.087324 -5.718280
2014-01-01 00:00:40 6878590.68 -3.763183 -14.175535 A -5.695685 -10.043372 1.481422 -5.803749
df[df["Spacecraft"] == "A"].head()
Radius Latitude Longitude Spacecraft F_res_CHAOS-Core B_NEC_res_CHAOS-Core_N B_NEC_res_CHAOS-Core_E B_NEC_res_CHAOS-Core_C
Timestamp
2014-01-01 00:00:00 6878309.29 -1.228937 -14.116675 A -6.749362 -9.918123 1.245212 -5.081174
2014-01-01 00:00:10 6878381.24 -1.862520 -14.131425 A -6.466947 -9.916732 2.133504 -5.603215
2014-01-01 00:00:20 6878452.12 -2.496089 -14.146156 A -6.206742 -9.927412 2.281760 -5.749293
2014-01-01 00:00:30 6878521.94 -3.129643 -14.160861 A -5.970891 -9.942918 2.087324 -5.718280
2014-01-01 00:00:40 6878590.68 -3.763183 -14.175535 A -5.695685 -10.043372 1.481422 -5.803749
df[df["Spacecraft"] == "C"].head()
Radius Latitude Longitude Spacecraft F_res_CHAOS-Core B_NEC_res_CHAOS-Core_N B_NEC_res_CHAOS-Core_E B_NEC_res_CHAOS-Core_C
Timestamp
2014-01-01 00:00:00 6877665.96 5.908083 -14.420068 C -10.489221 -10.648684 2.305189 -1.454687
2014-01-01 00:00:10 6877747.64 5.274386 -14.434576 C -10.073650 -10.401305 2.393188 -1.925914
2014-01-01 00:00:20 6877828.37 4.640702 -14.449141 C -9.738967 -10.288935 2.301424 -2.362902
2014-01-01 00:00:30 6877908.13 4.007031 -14.463755 C -9.481124 -10.389408 1.904433 -2.961317
2014-01-01 00:00:40 6877986.91 3.373371 -14.478411 C -9.215691 -10.472862 1.400795 -3.377475

… or using xarray#

ds = data.as_xarray()
ds.where(ds["Spacecraft"] == "A", drop=True)
<xarray.Dataset>
Dimensions:               (Timestamp: 360, NEC: 3)
Coordinates:
  * Timestamp             (Timestamp) datetime64[ns] 2014-01-01 ... 2014-01-0...
  * NEC                   (NEC) <U1 'N' 'E' 'C'
Data variables:
    Spacecraft            (Timestamp) object 'A' 'A' 'A' 'A' ... 'A' 'A' 'A' 'A'
    Radius                (Timestamp) float64 6.878e+06 6.878e+06 ... 6.868e+06
    Latitude              (Timestamp) float64 -1.229 -1.863 ... 48.14 48.77
    Longitude             (Timestamp) float64 -14.12 -14.13 ... 153.6 153.6
    B_NEC_res_CHAOS-Core  (Timestamp, NEC) float64 -9.918 1.245 ... 3.224 8.788
    F_res_CHAOS-Core      (Timestamp) float64 -6.749 -6.467 ... 3.699 3.681
Attributes:
    Sources:         ['SW_OPER_MAGA_LR_1B_20140101T000000_20140101T235959_060...
    MagneticModels:  ["CHAOS-Core = 'CHAOS-Core'(max_degree=20,min_degree=1)"]
    AppliedFilters:  []