FAC: Field-aligned currents#
The FAC toolbox provides the ability to calculate FACs on-demand, given magnetic measurements and model predictions.
Currently, the single-satellite algorithm is implemented and provides very similar output as the operational product SW_FACxTMS_2F. The results are not identical because of differences in the processing chain (for example, using the POMME model instead of the CHAOS model to supply the background magnetic field, or “mean field”)
For a description of the method, see:
Ritter, P., Lühr, H. & Rauberg, J. Determining field-aligned currents with the Swarm constellation mission. Earth Planet Sp 65, 1285–1294 (2013). https://doi.org/10.5047/eps.2013.09.006
For more sophisticated FAC estimates, see https://github.com/ablagau/SwarmFACE
import datetime as dt
import numpy as np
import matplotlib.pyplot as plt
from swarmpal.io import create_paldata, PalDataItem
from swarmpal.toolboxes import fac
Fetching data#
data_params = dict(
collection="SW_OPER_MAGA_LR_1B",
measurements=["B_NEC", "Flags_F", "Flags_B", "Flags_q"],
models=["CHAOS"],
start_time="2016-01-01T00:00:00",
end_time="2016-01-01T03:00:00",
server_url="https://vires.services/ows",
options=dict(asynchronous=False, show_progress=False),
)
data = create_paldata(PalDataItem.from_vires(**data_params))
print(data)
<xarray.DataTree 'paldata'>
Group: /
└── Group: /SW_OPER_MAGA_LR_1B
Dimensions: (Timestamp: 10800, NEC: 3)
Coordinates:
* Timestamp (Timestamp) datetime64[s] 86kB 2016-01-01 ... 2016-01-01T02:...
* NEC (NEC) <U1 12B 'N' 'E' 'C'
Data variables:
Spacecraft (Timestamp) object 86kB 'A' 'A' 'A' 'A' 'A' ... 'A' 'A' 'A' 'A'
Flags_B (Timestamp) uint8 11kB 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0
Latitude (Timestamp) float64 86kB -72.5 -72.56 -72.63 ... -44.97 -45.03
Longitude (Timestamp) float64 86kB 92.79 92.82 92.85 ... 41.83 41.83
Flags_F (Timestamp) uint8 11kB 1 1 1 1 1 1 1 1 1 ... 1 1 1 1 1 1 1 1 1
B_NEC_CHAOS (Timestamp, NEC) float64 259kB -1.602e+03 ... -2.568e+04
Radius (Timestamp) float64 86kB 6.834e+06 6.834e+06 ... 6.833e+06
B_NEC (Timestamp, NEC) float64 259kB -1.581e+03 ... -2.564e+04
Flags_q (Timestamp) uint8 11kB 5 5 5 5 5 5 5 5 5 ... 5 5 5 5 5 5 5 5 5
Attributes:
Sources: ['CHAOS-8.1_static.shc', 'SW_OPER_MAGA_LR_1B_20151231T00...
MagneticModels: ["CHAOS = 'CHAOS-Core'(max_degree=20,min_degree=1) + 'CH...
AppliedFilters: []
PAL_meta: {"analysis_window": ["2016-01-01T00:00:00", "2016-01-01T...
data.swarmpal.pal_meta
{'.': {},
'SW_OPER_MAGA_LR_1B': {'analysis_window': ['2016-01-01T00:00:00',
'2016-01-01T03:00:00'],
'magnetic_models': {'CHAOS': "'CHAOS-Core'(max_degree=20,min_degree=1) + 'CHAOS-Static'(max_degree=185,min_degree=21) + 'CHAOS-MMA-Primary'(max_degree=2,min_degree=1) + 'CHAOS-MMA-Secondary'(max_degree=2,min_degree=1)"},
'config': {'pad_times': [],
'collection': 'SW_OPER_MAGA_LR_1B',
'measurements': ['B_NEC', 'Flags_F', 'Flags_B', 'Flags_q'],
'start_time': '2016-01-01T00:00:00',
'end_time': '2016-01-01T03:00:00',
'server_url': 'https://vires.services/ows',
'models': ['CHAOS'],
'auxiliaries': [],
'sampling_step': None,
'filters': [],
'options': {'asynchronous': False, 'show_progress': False},
'provider': 'vires'}}}
Applying a process and viewing the results#
process = fac.processes.FAC_single_sat(
config={
"dataset": "SW_OPER_MAGA_LR_1B",
"model_varname": "B_NEC_CHAOS",
"measurement_varname": "B_NEC",
},
)
data = process(data)
print(data)
<xarray.DataTree 'paldata'>
Group: /
│ Attributes:
│ PAL_meta: {"output_datasets": ["PAL_FAC_single_sat"]}
├── Group: /SW_OPER_MAGA_LR_1B
│ Dimensions: (Timestamp: 10800, NEC: 3)
│ Coordinates:
│ * Timestamp (Timestamp) datetime64[s] 86kB 2016-01-01 ... 2016-01-01T02:...
│ * NEC (NEC) <U1 12B 'N' 'E' 'C'
│ Data variables:
│ Spacecraft (Timestamp) object 86kB 'A' 'A' 'A' 'A' 'A' ... 'A' 'A' 'A' 'A'
│ Flags_B (Timestamp) uint8 11kB 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0
│ Latitude (Timestamp) float64 86kB -72.5 -72.56 -72.63 ... -44.97 -45.03
│ Longitude (Timestamp) float64 86kB 92.79 92.82 92.85 ... 41.83 41.83
│ Flags_F (Timestamp) uint8 11kB 1 1 1 1 1 1 1 1 1 ... 1 1 1 1 1 1 1 1 1
│ B_NEC_CHAOS (Timestamp, NEC) float64 259kB -1.602e+03 ... -2.568e+04
│ Radius (Timestamp) float64 86kB 6.834e+06 6.834e+06 ... 6.833e+06
│ B_NEC (Timestamp, NEC) float64 259kB -1.581e+03 ... -2.564e+04
│ Flags_q (Timestamp) uint8 11kB 5 5 5 5 5 5 5 5 5 ... 5 5 5 5 5 5 5 5 5
│ Attributes:
│ Sources: ['CHAOS-8.1_static.shc', 'SW_OPER_MAGA_LR_1B_20151231T00...
│ MagneticModels: ["CHAOS = 'CHAOS-Core'(max_degree=20,min_degree=1) + 'CH...
│ AppliedFilters: []
│ PAL_meta: {"analysis_window": ["2016-01-01T00:00:00", "2016-01-01T...
└── Group: /PAL_FAC_single_sat
Dimensions: (Timestamp: 10799)
Coordinates:
* Timestamp (Timestamp) datetime64[ns] 86kB 2016-01-01T00:00:00.500000 ......
Data variables:
FAC (Timestamp) float64 86kB -0.1344 -0.04132 ... 0.005725 -0.007201
IRC (Timestamp) float64 86kB -0.131 -0.04028 ... 0.005101 -0.006416
Flags_B (Timestamp) uint8 11kB 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0
Latitude (Timestamp) float64 86kB nan nan nan nan nan ... nan nan nan nan
Longitude (Timestamp) float64 86kB nan nan nan nan nan ... nan nan nan nan
Flags_F (Timestamp) uint8 11kB 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0
Radius (Timestamp) float64 86kB nan nan nan nan nan ... nan nan nan nan
Flags_q (Timestamp) uint8 11kB 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0
Attributes:
Sources: ['CHAOS-8.1_static.shc', 'SW_OPER_MAGA_LR_1B_20151231T000000_2...
PAL_meta: {"FAC_single_sat": {"output_dataset": "PAL_FAC_single_sat", "d...
/home/docs/checkouts/readthedocs.org/user_builds/swarmpal/envs/main/lib/python3.11/site-packages/xarray/core/duck_array_ops.py:264: RuntimeWarning: invalid value encountered in cast
return data.astype(dtype, **kwargs)
data.swarmpal.pal_meta
{'.': {'output_datasets': ['PAL_FAC_single_sat']},
'SW_OPER_MAGA_LR_1B': {'analysis_window': ['2016-01-01T00:00:00',
'2016-01-01T03:00:00'],
'magnetic_models': {'CHAOS': "'CHAOS-Core'(max_degree=20,min_degree=1) + 'CHAOS-Static'(max_degree=185,min_degree=21) + 'CHAOS-MMA-Primary'(max_degree=2,min_degree=1) + 'CHAOS-MMA-Secondary'(max_degree=2,min_degree=1)"},
'config': {'pad_times': [],
'collection': 'SW_OPER_MAGA_LR_1B',
'measurements': ['B_NEC', 'Flags_F', 'Flags_B', 'Flags_q'],
'start_time': '2016-01-01T00:00:00',
'end_time': '2016-01-01T03:00:00',
'server_url': 'https://vires.services/ows',
'models': ['CHAOS'],
'auxiliaries': [],
'sampling_step': None,
'filters': [],
'options': {'asynchronous': False, 'show_progress': False},
'provider': 'vires'}},
'PAL_FAC_single_sat': {'FAC_single_sat': {'output_dataset': 'PAL_FAC_single_sat',
'dataset': 'SW_OPER_MAGA_LR_1B',
'model_varname': 'B_NEC_CHAOS',
'measurement_varname': 'B_NEC',
'inclination_limit': 30,
'time_jump_limit': 1,
'include_auxiliaries': True}}}
print(data["PAL_FAC_single_sat"])
<xarray.DataTree 'PAL_FAC_single_sat'>
Group: /PAL_FAC_single_sat
Dimensions: (Timestamp: 10799)
Coordinates:
* Timestamp (Timestamp) datetime64[ns] 86kB 2016-01-01T00:00:00.500000 ......
Data variables:
FAC (Timestamp) float64 86kB -0.1344 -0.04132 ... 0.005725 -0.007201
IRC (Timestamp) float64 86kB -0.131 -0.04028 ... 0.005101 -0.006416
Flags_B (Timestamp) uint8 11kB 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0
Latitude (Timestamp) float64 86kB nan nan nan nan nan ... nan nan nan nan
Longitude (Timestamp) float64 86kB nan nan nan nan nan ... nan nan nan nan
Flags_F (Timestamp) uint8 11kB 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0
Radius (Timestamp) float64 86kB nan nan nan nan nan ... nan nan nan nan
Flags_q (Timestamp) uint8 11kB 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0
Attributes:
Sources: ['CHAOS-8.1_static.shc', 'SW_OPER_MAGA_LR_1B_20151231T000000_2...
PAL_meta: {"FAC_single_sat": {"output_dataset": "PAL_FAC_single_sat", "d...
data.swarmpal_fac.quicklook();
Retrying with data subselection#
This time we will fetch data with a filter applied to the request from VirES
See viresclient.SwarmRequest.add_filter for how these behave. swarmpal.io.PalDataItem.from_vires accepts a list of such filters.
NB. there is currently a bug requiring that each filter string is enclosed in parentheses.
data_params = dict(
collection="SW_OPER_MAGA_LR_1B",
measurements=["B_NEC"],
models=["CHAOS"],
start_time="2016-01-01T00:00:00",
end_time="2016-01-01T03:00:00",
server_url="https://vires.services/ows",
options=dict(asynchronous=False, show_progress=False),
filters=[
"((QDLat > 50) OR (QDLat < -50))", # the algorithm is only valid at high latitude
"(Flags_B <= 9)", # Exclude particularly bad data
],
)
data = create_paldata(PalDataItem.from_vires(**data_params))
data = data.swarmpal.apply(process)
data.swarmpal_fac.quicklook();
Comparing with FAC product#
# Fetch the input to the FAC process...
data_params_fac_input = dict(
collection="SW_OPER_MAGA_LR_1B",
measurements=["B_NEC"],
models=["CHAOS"],
start_time="2016-01-01T00:00:00",
end_time="2016-01-01T03:00:00",
server_url="https://vires.services/ows",
options=dict(asynchronous=False, show_progress=False),
)
# ... and the FAC product itself
data_params_fac_product = dict(
collection="SW_OPER_FACATMS_2F",
measurements=["IRC", "FAC"],
start_time="2016-01-01T00:00:00",
end_time="2016-01-01T03:00:00",
server_url="https://vires.services/ows",
options=dict(asynchronous=False, show_progress=False),
)
data = create_paldata(
PalDataItem.from_vires(**data_params_fac_input),
PalDataItem.from_vires(**data_params_fac_product),
)
# Apply the FAC process
process = fac.processes.FAC_single_sat(
config={
"dataset": "SW_OPER_MAGA_LR_1B",
"model_varname": "B_NEC_CHAOS",
"measurement_varname": "B_NEC",
},
)
data = process(data)
# Plot comparing them
fig, axes = plt.subplots(nrows=3, figsize=(15, 10), sharex=True)
data["PAL_FAC_single_sat"]["IRC"].plot(ax=axes[0])
data["SW_OPER_FACATMS_2F"]["IRC"].plot(ax=axes[1])
(data["PAL_FAC_single_sat"]["IRC"] - data["SW_OPER_FACATMS_2F"]["IRC"]).plot(ax=axes[2])
axes[0].set_ylabel(f"SwarmPAL on-demand\n{axes[0].get_ylabel()}")
axes[1].set_ylabel(f"Official product\n{axes[1].get_ylabel()}")
axes[2].set_ylabel("Difference between the above")
for ax in axes:
ax.grid()
ax.set_xlabel("")