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.14.1
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',
 'CHAOS-MIO',
 '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:00, 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_0701_MDR_MAG_LR',
 'SW_OPER_MCO_SHA_2D_20131126T000000_20180101T000000_0401',
 'SW_OPER_MCO_SHA_2X_19970101T000000_20260205T235959_0804']

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()
Radius Latitude F Spacecraft Longitude F_CHAOS-Core F_MCO_SHA_2D B_NEC_CHAOS-Core_N B_NEC_CHAOS-Core_E B_NEC_CHAOS-Core_C B_NEC_MCO_SHA_2D_N B_NEC_MCO_SHA_2D_E B_NEC_MCO_SHA_2D_C B_NEC_N B_NEC_E B_NEC_C
Timestamp
2014-01-01 00:00:00 6878309.23 -1.228938 22867.4800 A -14.116674 22874.128454 22874.211386 20113.339737 -4127.289065 -10081.904687 20113.623790 -4127.463942 -10081.454557 20103.4954 -4125.6029 -10086.8732
2014-01-01 00:00:10 6878381.18 -1.862521 22814.4928 A -14.131424 22820.853746 22820.941311 19824.865268 -4162.944027 -10508.852449 19825.161759 -4163.127529 -10508.410571 19815.0284 -4160.3663 -10514.3464
2014-01-01 00:00:20 6878452.06 -2.496090 22763.1833 A -14.146155 22769.278793 22769.369047 19533.247324 -4197.337695 -10921.293973 19533.553823 -4197.529033 -10920.860399 19523.4054 -4194.6083 -10926.9383
2014-01-01 00:00:30 6878521.88 -3.129644 22713.2926 A -14.160861 22719.147101 22719.238127 19239.029090 -4230.403300 -11319.147167 19239.343493 -4230.601798 -11318.721282 19229.1766 -4227.8647 -11324.7658
2014-01-01 00:00:40 6878590.62 -3.763184 22664.6401 A -14.175534 22670.214548 22670.304568 18942.754418 -4262.075495 -11702.367077 18943.075066 -4262.280613 -11701.947711 18932.8056 -4260.1382 -11708.0771

… 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_MCO_SHA_2D  (Timestamp, NEC) float64 2.011e+04 ... 3.557e+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
    B_NEC_CHAOS-Core  (Timestamp, NEC) float64 2.011e+04 ... 3.557e+04
    Longitude         (Timestamp) float64 -14.12 -14.13 -14.15 ... 153.6 153.6
    F_CHAOS-Core      (Timestamp) float64 2.287e+04 2.282e+04 ... 4.02e+04
    F_MCO_SHA_2D      (Timestamp) float64 2.287e+04 2.282e+04 ... 4.021e+04
    B_NEC             (Timestamp, NEC) float64 2.01e+04 -4.126e+03 ... 3.558e+04
Attributes:
    Sources:         ['SW_OPER_MAGA_LR_1B_20140101T000000_20140101T235959_070...
    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:00, Remaining: 00:00 ] [1/1]
Downloading: 100%
[ Elapsed: 00:00, Remaining: 00:00 ] (0.081MB)
F_res_CHAOS-Core Radius Latitude Spacecraft Longitude F_res_MCO_SHA_2D B_NEC_res_CHAOS-Core_N B_NEC_res_CHAOS-Core_E B_NEC_res_CHAOS-Core_C B_NEC_res_MCO_SHA_2D_N B_NEC_res_MCO_SHA_2D_E B_NEC_res_MCO_SHA_2D_C
Timestamp
2014-01-01 00:00:00 -6.648454 6878309.23 -1.228938 A -14.116674 -6.731386 -9.844337 1.686165 -4.968513 -10.128390 1.861042 -5.418643
2014-01-01 00:00:10 -6.360946 6878381.18 -1.862521 A -14.131424 -6.448511 -9.836868 2.577727 -5.493951 -10.133359 2.761229 -5.935829
2014-01-01 00:00:20 -6.095493 6878452.06 -2.496090 A -14.146155 -6.185747 -9.841924 2.729395 -5.644327 -10.148423 2.920733 -6.077901
2014-01-01 00:00:30 -5.854501 6878521.88 -3.129644 A -14.160861 -5.945527 -9.852490 2.538600 -5.618633 -10.166893 2.737098 -6.044518
2014-01-01 00:00:40 -5.574448 6878590.62 -3.763184 A -14.175534 -5.664468 -9.948818 1.937295 -5.710023 -10.269466 2.142413 -6.129389

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/0c22d1fdf0ea5e6a2203365d2b53e69b2063efaf4a387b0e052b940f59d22288.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/5575cabedd58d3d01c782fd4454694053f293d23f8607f36538ed5c4bbb59b2b.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/5575cabedd58d3d01c782fd4454694053f293d23f8607f36538ed5c4bbb59b2b.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/f38658885687bffb70af6eb8328588c31c65ea93f1feed2f7b9af0d403331394.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'
    F_res_CHAOS-Core      (Timestamp) float64 -6.648 -6.361 ... 3.905 3.881
    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.844 1.686 ... 3.83 8.914
    F_res_MCO_SHA_2D      (Timestamp) float64 -6.731 -6.449 ... 3.122 3.076
    B_NEC_res_MCO_SHA_2D  (Timestamp, NEC) float64 -10.13 1.861 ... 3.677 9.109
Attributes:
    Sources:         ['SW_OPER_MAGA_LR_1B_20140101T000000_20140101T235959_070...
    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/4e1a932c645c163a17a51a89e281e49a6cd9585cc5fd5e9e40c2da992928375a.png

Note that xarray also allows convenient direct plotting like:

ds["B_NEC_res_CHAOS-Core"].plot.line(x="Timestamp");
../_images/2cac414279db259aaef6248229d0ccc83c85a9ef4e4bf21593fb4e09230c5b2d.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:00, Remaining: 00:00 ] [1/1]
Downloading: 100%
[ Elapsed: 00:00, Remaining: 00:00 ] (0.072MB)
F_res_CHAOS-Core Radius Latitude Spacecraft Longitude 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.648454 6878309.23 -1.228938 A -14.116674 -9.844337 1.686165 -4.968513
2014-01-01 00:00:10 -6.360946 6878381.18 -1.862521 A -14.131424 -9.836868 2.577727 -5.493951
2014-01-01 00:00:20 -6.095493 6878452.06 -2.496090 A -14.146155 -9.841924 2.729395 -5.644327
2014-01-01 00:00:30 -5.854501 6878521.88 -3.129644 A -14.160861 -9.852490 2.538600 -5.618633
2014-01-01 00:00:40 -5.574448 6878590.62 -3.763184 A -14.175534 -9.948818 1.937295 -5.710023
df[df["Spacecraft"] == "A"].head()
F_res_CHAOS-Core Radius Latitude Spacecraft Longitude 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.648454 6878309.23 -1.228938 A -14.116674 -9.844337 1.686165 -4.968513
2014-01-01 00:00:10 -6.360946 6878381.18 -1.862521 A -14.131424 -9.836868 2.577727 -5.493951
2014-01-01 00:00:20 -6.095493 6878452.06 -2.496090 A -14.146155 -9.841924 2.729395 -5.644327
2014-01-01 00:00:30 -5.854501 6878521.88 -3.129644 A -14.160861 -9.852490 2.538600 -5.618633
2014-01-01 00:00:40 -5.574448 6878590.62 -3.763184 A -14.175534 -9.948818 1.937295 -5.710023
df[df["Spacecraft"] == "C"].head()
F_res_CHAOS-Core Radius Latitude Spacecraft Longitude 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 -10.406604 6877665.96 5.908082 C -14.420068 -10.528669 2.376904 -1.288431
2014-01-01 00:00:10 -9.994638 6877747.64 5.274386 C -14.434576 -10.280421 2.472138 -1.754322
2014-01-01 00:00:20 -9.662760 6877828.36 4.640702 C -14.449141 -10.165773 2.387337 -2.185927
2014-01-01 00:00:30 -9.406505 6877908.12 4.007030 C -14.463755 -10.262536 1.997575 -2.779252
2014-01-01 00:00:40 -9.141763 6877986.90 3.373371 C -14.478412 -10.340842 1.502170 -3.190721

… 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'
    F_res_CHAOS-Core      (Timestamp) float64 -6.648 -6.361 ... 3.905 3.881
    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.844 1.686 ... 3.83 8.914
Attributes:
    Sources:         ['SW_OPER_MAGA_LR_1B_20140101T000000_20140101T235959_070...
    MagneticModels:  ["CHAOS-Core = 'CHAOS-Core'(max_degree=20,min_degree=1)"]
    AppliedFilters:  []