AOBxFAC (Auroral oval boundaries)#
Abstract: Access to the AEBS project output, boundaries of the auroral oval determined from field-aligned currents
See also:
https://nbviewer.jupyter.org/github/pacesm/jupyter_notebooks/blob/master/AEBS/AEBS_AOB_FAC.ipynb
https://earth.esa.int/eogateway/search?filter=swarm&text=aebs
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");
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();
TODO
Only fill between times where well determined (break up into segments which are contiguous and do many sequential fill_between’s)