AOBxFAC (Auroral oval boundaries)

AOBxFAC (Auroral oval boundaries)#

Abstract: Access to the AEBS project output, boundaries of the auroral oval determined from field-aligned currents

See also:

Accessing the collection#

from viresclient import SwarmRequest
import datetime as dt
import matplotlib.pyplot as plt
request = SwarmRequest()
request.available_collections("AOB_FAC", details=False)
{'AOB_FAC': ['SW_OPER_AOBAFAC_2F', 'SW_OPER_AOBBFAC_2F', 'SW_OPER_AOBCFAC_2F']}
request.available_measurements("AOB_FAC")
['Latitude_QD',
 'Longitude_QD',
 'MLT_QD',
 'Boundary_Flag',
 'Quality',
 'Pair_Indicator']

Let’s fetch one month of measurements. We will also fetch magnetic coordinates and the orbit direction flag for extra context, which we will use later in the figure.

request.set_collection("SW_OPER_AOBAFAC_2F")
request.set_products(
    ['Latitude_QD',
     'Longitude_QD',
     'MLT_QD',
     'Boundary_Flag',
     'Quality',
     'Pair_Indicator'],
    auxiliaries=["QDOrbitDirection", "QDLat", "QDLon", "MLT"]
)
data = request.get_between(
    dt.datetime(2016, 1, 1),
    dt.datetime(2016, 2, 1)
)
ds = data.as_xarray()
# Remove the Sources attribute now to keep the notebook cleaner
ds.attrs.pop("Sources")
ds
<xarray.Dataset>
Dimensions:           (Timestamp: 3157, Quality_dim1: 2)
Coordinates:
  * Timestamp         (Timestamp) datetime64[ns] 2016-01-01T00:07:01.500000 ....
Dimensions without coordinates: Quality_dim1
Data variables: (12/14)
    Spacecraft        (Timestamp) object 'A' 'A' 'A' 'A' 'A' ... 'A' 'A' 'A' 'A'
    MLT_QD            (Timestamp) float64 18.61 18.04 17.11 ... 1.216 0.2201
    QDLon             (Timestamp) float64 -5.015 -14.53 -36.0 ... 99.16 83.28
    Radius            (Timestamp) float64 6.834e+06 6.834e+06 ... 6.834e+06
    Pair_Indicator    (Timestamp) int8 1 -1 1 -1 1 -1 1 -1 ... -1 1 -1 1 -1 1 -1
    Longitude_QD      (Timestamp) float64 -5.015 -14.53 -36.0 ... 99.16 83.28
    ...                ...
    QDOrbitDirection  (Timestamp) int8 1 1 1 1 -1 -1 -1 -1 ... 1 1 1 -1 -1 -1 -1
    MLT               (Timestamp) float64 18.61 18.04 17.11 ... 1.216 0.2201
    Quality           (Timestamp, Quality_dim1) float64 3.572 0.1437 ... 0.2844
    QDLat             (Timestamp) float64 -67.88 -53.31 56.62 ... -57.92 -68.26
    Boundary_Flag     (Timestamp) uint8 2 1 1 2 2 1 1 2 2 ... 2 2 1 1 2 2 1 1 2
    Latitude          (Timestamp) float64 -80.0 -64.82 48.04 ... -53.34 -67.73
Attributes:
    MagneticModels:  []
    AppliedFilters:  []

Visualising the data#

We can filter out for orbital segments where the spacecraft is approaching the pole (i.e. roughly within the same local time sector) using the QDOrbitDirection flag, and identify only the equatorward boundaries of the detected auroral oval using the Boundary_Flag like so:

ds.where(
    (ds["Boundary_Flag"]==1) & (ds["QDOrbitDirection"] == 1) & (ds["Latitude"] < 0)
).dropna(dim="Timestamp")["QDLat"].plot.line(x="Timestamp");
../_images/4406e3b24b1d4285472e336d649320f9eb2e6267088099d9a695de91f8676687.png

That shows us one edge of the auroral oval. Now let’s expand this idea to show both edges (filling between them), for both sectors of the auroral oval crossing (toward and away from the pole), for both Northern and Southern hemispheres.

ds = ds.where(ds["Pair_Indicator"] != 0)

northern_poleward_boundaries_ascendingsector = ds.where(
    (ds["Latitude"] > 0) & (ds["Boundary_Flag"] == 2) & (ds["QDOrbitDirection"] == 1)
).dropna(dim="Timestamp")
northern_equatorward_boundaries_ascendingsector = ds.where(
    (ds["Latitude"] > 0) & (ds["Boundary_Flag"] == 1) & (ds["QDOrbitDirection"] == 1)
).dropna(dim="Timestamp")

northern_poleward_boundaries_descendingsector = ds.where(
    (ds["Latitude"] > 0) & (ds["Boundary_Flag"] == 2) & (ds["QDOrbitDirection"] == -1)
).dropna(dim="Timestamp")
northern_equatorward_boundaries_descendingsector = ds.where(
    (ds["Latitude"] > 0) & (ds["Boundary_Flag"] == 1) & (ds["QDOrbitDirection"] == -1)
).dropna(dim="Timestamp")

southern_poleward_boundaries_ascendingsector = ds.where(
    (ds["Latitude"] < 0) & (ds["Boundary_Flag"] == 2) & (ds["QDOrbitDirection"] == 1)
).dropna(dim="Timestamp")
southern_equatorward_boundaries_ascendingsector = ds.where(
    (ds["Latitude"] < 0) & (ds["Boundary_Flag"] == 1) & (ds["QDOrbitDirection"] == 1)
).dropna(dim="Timestamp")

southern_poleward_boundaries_descendingsector = ds.where(
    (ds["Latitude"] < 0) & (ds["Boundary_Flag"] == 2) & (ds["QDOrbitDirection"] == -1)
).dropna(dim="Timestamp")
southern_equatorward_boundaries_descendingsector = ds.where(
    (ds["Latitude"] < 0) & (ds["Boundary_Flag"] == 1) & (ds["QDOrbitDirection"] == -1)
).dropna(dim="Timestamp")
fig, axes = plt.subplots(nrows=2, ncols=1, figsize=(15, 5), sharex=True)
axes[0].fill_between(
    x=northern_equatorward_boundaries_descendingsector["Timestamp"],
    y1=northern_equatorward_boundaries_descendingsector["QDLat"],
    y2=northern_poleward_boundaries_descendingsector["QDLat"],
    color="tab:blue",
    alpha=0.5
)
axes[0].fill_between(
    x=southern_equatorward_boundaries_descendingsector["Timestamp"],
    y1=-southern_equatorward_boundaries_descendingsector["QDLat"],
    y2=-southern_poleward_boundaries_descendingsector["QDLat"],
    color="tab:red",
    alpha=0.5
)

axes[1].fill_between(
    x=northern_equatorward_boundaries_ascendingsector["Timestamp"],
    y1=northern_equatorward_boundaries_ascendingsector["QDLat"],
    y2=northern_poleward_boundaries_ascendingsector["QDLat"],
    color="tab:blue",
    alpha=0.5,
    label="North"
)
axes[1].fill_between(
    x=southern_equatorward_boundaries_ascendingsector["Timestamp"],
    y1=-southern_equatorward_boundaries_ascendingsector["QDLat"],
    y2=-southern_poleward_boundaries_ascendingsector["QDLat"],
    color="tab:red",
    alpha=0.5,
    label="South"
)

axes[0].set_ylim((90, 50))
axes[1].set_ylim((50, 90))
fig.subplots_adjust(hspace=0)
axes[1].set_ylabel("|QDLat| [deg]\nAscending sector");
axes[0].set_ylabel("|QDLat| [deg]\nDescending sector");
axes[1].legend();
../_images/d613d7e56d00c2501c88e04817914d993d5661c6c1d8ef5925b3cae80ba35b2a.png

TODO

  • Only fill between times where well determined (break up into segments which are contiguous and do many sequential fill_between’s)