IBP-Model#

Notebook authors: Ina Rusch

Package authors: Martin Rother, Ina Rusch

Resources#

Python package: ibpmodel
Code
Documentation
Swarm Data Handbook

Basic usage#

import ibpmodel as ibp
import numpy as np
import matplotlib.pyplot as plt

Calculate the IBP model at given time of year, position, and conditions:

ibp.calculateIBPindex(
    day_month=15,       # Day of Year or Month
    longitude=0,        # Longitude in degree
    local_time=20.9,    # Local time in hours
    f107=150,            # F10.7 cm Solar Flux index
)
Doy Month Lon LT F10.7 IBP
0 15 1 0 20.9 150 0.3547
ibp.calculateIBPindex(day_month=['Jan','Feb','Mar'], local_time=22)
Doy Month Lon LT F10.7 IBP
0 15 1 -180 22 150 0.0739
1 15 1 -175 22 150 0.0722
2 15 1 -170 22 150 0.0717
3 15 1 -165 22 150 0.0728
4 15 1 -160 22 150 0.0771
... ... ... ... ... ... ...
211 74 3 155 22 150 0.2061
212 74 3 160 22 150 0.2025
213 74 3 165 22 150 0.1994
214 74 3 170 22 150 0.1967
215 74 3 175 22 150 0.1943

216 rows × 6 columns

Plot the global pattern for a given day:

ibp.plotIBPindex(doy=349)
../_images/867bbc30a1e5d8b10cede225dccd999d90a5d09329225e6668b9ec136abdc594.png

Plot the month vs longitude pattern:

ibp.plotButterflyData(f107=150)
../_images/56c35ab65a6f4ae9154457aecbd52c91b95e460b53be6746ebeaed72b6387163.png

More examples#

ibp.calculateIBPindex('Jan', longitude=170, local_time=np.arange(-2,-1,0.1))
Doy Month Lon LT F10.7 IBP
0 15 1 170 -2.0 150 0.0814
1 15 1 170 -1.9 150 0.0830
2 15 1 170 -1.8 150 0.0843
3 15 1 170 -1.7 150 0.0850
4 15 1 170 -1.6 150 0.0853
5 15 1 170 -1.5 150 0.0852
6 15 1 170 -1.4 150 0.0846
7 15 1 170 -1.3 150 0.0837
8 15 1 170 -1.2 150 0.0823
9 15 1 170 -1.1 150 0.0806
ibp.calculateIBPindex(day_month=[1,15,31], longitude=[-170,175,170], local_time=0, f107=150)
Doy Month Lon LT F10.7 IBP
0 1 1 -170 0 150 0.0411
1 1 1 175 0 150 0.0456
2 1 1 170 0 150 0.0486
3 15 1 -170 0 150 0.0430
4 15 1 175 0 150 0.0477
5 15 1 170 0 150 0.0508
6 31 1 -170 0 150 0.0478
7 31 1 175 0 150 0.0529
8 31 1 170 0 150 0.0563
ibp.plotIBPindex('Feb', cmap='viridis', alpha=0.9)
../_images/15cb8cc59019347675126dd54f2183d9c40885de8bdb44c80e589a3b24421206.png
doys = [319, 349, 15, 46]
fig_doys, axes_doys = plt.subplots(len(doys),1, layout='constrained',figsize=(9, 15))
    
for d, ax in zip(doys, axes_doys):
    ax, scalarmap = ibp.plotIBPindex(d, ax=ax)

ibp.ibpforward.setcolorbar(scalarmap, fig_doys, axes_doys, fraction=0.05)
plt.show()
../_images/c5965c1806b52786a603eb5fbc08e94d67f4ed5e7cd3c312f55754bb586a8674.png
solar = [90, 130, 180]
doy = 349
fig_solar, axes_solar = plt.subplots(len(solar),1, layout='constrained',figsize=(9, 13))
fig_solar.suptitle(f"Doy {doy} with different solar radio flux", fontsize=16)

for f, ax in zip(solar, axes_solar):
    ax, scalarmap = ibp.plotIBPindex(doy, f107=f, ax=ax)
    ax.set_title(f"IBP by solar radio flux {f}")

cbar = ibp.ibpforward.setcolorbar(scalarmap, fig_solar, axes_solar, fraction=0.05, orientation='horizontal')
cbar.set_label('new label name')
../_images/ed869af73f8722b94be424ef5080a757a9a7db18315c5c2036e251cfd7358670.png
ibp.plotButterflyData(150, cmap='magma')
../_images/25c5672fc9249fcf3e98e6f07bdb03bb62fa82c5d08b681e42e33dfd00f23868.png
solar = [130, 180]
fig_bfly, axes_bfly = plt.subplots(1,len(solar), layout='constrained',figsize=(12.4, 5))
    
for b, ax in zip(solar, axes_bfly):
    ax, scalarmap = ibp.plotButterflyData(b, ax=ax)

ibp.ibpforward.setcolorbar(scalarmap, fig_bfly, axes_bfly)
plt.show()
../_images/dcf2b7f6305f0e21dd24be3685af1ee165dfe26d2c46970257a2fe749ed507dd.png

Visualisation along Swarm orbits#

Source:
https://igit.iap-kborn.de/ibp/ibp-model/-/blob/main/template/IBP-VirES.ipynb

%pip install --quiet lxml
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager, possibly rendering your system unusable.It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv. Use the --root-user-action option if you know what you are doing and want to suppress this warning.

Note: you may need to restart the kernel to use updated packages.
import ibpmodel as ibp
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from datetime import datetime, timezone, timedelta, date
from viresclient import SwarmRequest
def getlt(datum, lon):
    '''Output of the day of the year and local time for specifying date(s) and longitude(s).
    
    Parameters
    ----------
    datum : datetime or list of datetimes
    lon : int or list of int
        The geographical longitude, ``-180 <= longitude <= 180``.
    
    Returns
    -------
    tupel
        contains (list of) day(s) of the year and (list of) local time(s)
    '''
    def calclt(date, lon):
        return round((date.hour + date.minute/60 + date.second/3600 + 24*lon/360) % 24, 1)
    
    if isinstance(datum, datetime):
        doys = int(datum.strftime('%j'))
        lts = calclt(datum, lon)

    elif isinstance(datum, list):
        doys = []
        lts = []
        for d, l in zip(datum, lon):
            doys.append(int(d.strftime('%j')))
            lts.append(calclt(d,l))
        
    return (doys, lts)


def loadsatellitedata(starttime, endtime, satellite):
    '''Load data via viresclient
    
    Parameters
    ----------
    starttime : datetime
    endtime : datetime
    satellite : str
        name of satellite 
        
    Returns
    -------
    pandas.DataFrame
        
    '''
    request = SwarmRequest()
    request.set_collection(f"SW_OPER_IBI{satellite}TMS_2F")
    request.set_products(measurements=request.available_measurements("IBI"), 
                         auxiliaries=['F10_INDEX', 'F107'])
    data = request.get_between(starttime, endtime)
    df = data.as_dataframe()
    df = df[ (df.Bubble_Index > -1) ]
    
    return df


def downloadfn(dates):
    '''Download Solar Radio Flux for specified date(s).
    
    Parameters
    ----------
    dates : datetime or list of datetimes
    
    Returns
    -------
    list of numpy.float64
        Solar Radio Flux
    '''
    def loaddf(year):
        df = pd.read_html(f'https://spaceweather.gc.ca/forecast-prevision/solar-solaire/solarflux/sx-5-flux-en.php?year={year}')[0]
        return df
    
    def getfn(df,date):
        return df[ (df.Date == date.strftime("%Y-%m-%d")) & (df.Time == "20:00:00") ]["Observed Flux"].values[0]
    
    if isinstance(dates, datetime):
        years = [ dates.year ]
        dates = [ dates ]
    elif isinstance(dates, list):
        years = [ dates[0].year ]
    else: 
        raise TypeError(f"must be datetime or list of datetimes")
        
    df_fn = loaddf(years[0])
    fn = []
    
    for d in dates:
        y = d.year
        if y not in years:
            df_fn = pd.concat([df_fn, loaddf(y)])
            fn.append(getfn(df_fn, d))
            years.append(y) 
        else:
            fn.append(getfn(df_fn, d))
       
    return fn


def calcindex(df):
    '''Calculation of the IBP index. If Solar Radio Flux lower than 80 the IBP index is set to -1.
    
    Parameters
    ----------
    df : pandas.DataFrame (with columns Doy, Lon, LT and F10.7)
    
    Returns
    -------
    pandas.DataFrame
        contains the columns: Doy (Day(s) of the year), Month (Month(s) from the day of the year), 
        Lon (Longitude(s)), LT (Local Time(s)), F10.7 (solar index(es)), IBP (Ionospheric Bubble Index, value(s) between 0.0 and 1.0).
    '''
    def calcibp(index, row):
        df = ibp.calculateIBPindex(day_month=int(row["Doy"]), longitude=row["Lon"], local_time=row["LT"], f107=row["F10.7"])
        df = df.set_index([pd.Index([index])])
        return df
    
    index_sort = df.index
    
    tmp = df[ (df["F10.7"] < 80) ].copy()
    tmp["IBP"] = -1
    tmp['Month'] = [ int(datetime.strptime(f"2023-{t}","%Y-%j").strftime('%m')) for t in tmp.Doy ]
    tmp['Month'] = tmp['Month'].astype(int)
    
    di = pd.DataFrame(columns=tmp.columns)
    
    i = 0
    for index, row in df[ (df["F10.7"] >= 80) ].iterrows():
        if i == 0:
            di = calcibp(index, row)
            i = 1
        else:
            ib = calcibp(index, row)
            di = pd.concat([di, ib])

    di = pd.concat([di, tmp])
    di = di.reindex(index_sort)
    
    return di[['Doy','Month','Lon','LT','F10.7','IBP']]


def satelliteIBP(df):
    '''Calculation of the IBP index at the specified time and longitude
    
    Parameters
    ----------
    df : pandas.DataFrame (with DatetimeIndex and columns Longitude and F107)
        
    Returns
    -------
    pandas.DataFrame
        with column IBP (Ionospheric Bubble Index, values between 0.0 and 1.0)

    '''
  
    # Create required columns
    df['Lon'] = np.rint(df['Longitude'])
    df['dt'] = df.index
    df['timediff'] = ((df.dt - df.dt.shift()) / np.timedelta64(1, 'm')).gt(3).cumsum()
    df['londiff'] = np.absolute(df['Lon'] - df['Longitude'])

    tmp = df.loc[df.groupby(["timediff", "Lon"]).agg({"londiff": ['idxmin']})[('londiff', 'idxmin')].values]
    tmp["Doy"], tmp["LT"] = getlt(tmp.index.tolist(), tmp.Lon)
    
    tmp.rename(columns={"F107": "F10.7"}, inplace=True)
    
    tmp['IBP'] = calcindex(tmp[['Doy', 'LT', 'Lon', 'F10.7']])['IBP']
    
    # merge df and tmp
    df = pd.concat([df, tmp['IBP']], axis=1)
    del df["Lon"], df["dt"], df["londiff"], df["timediff"]
    
    return df


def globalIBP(datum, fn=150):
    '''Calculation of the IBP index for all longitudes at the specified time.
    
    Parameters
    ----------
    datum : datetime
        
    Returns
    -------
    pandas.DataFrame
        contains the columns: Doy (Day of the year), Month (Month from the day of the year), 
        Lon (Longitudes), LT (Local Times), F10.7 (solar index), IBP (Ionospheric Bubble Index, values between 0.0 and 1.0).
    '''
    df = pd.DataFrame(columns=["Doy", "Lon", "LT", "F10.7"])
    df["Lon"] = np.arange(-180,180)
    df["Doy"], df["LT"] = getlt(datum, df["Lon"])
    
    if datum.date() >= datetime.now().replace(tzinfo=timezone.utc).date():
        df["F10.7"] = fn
    else:
        df["F10.7"] = downloadfn(datum)[0]
    
    return calcindex(df)

IBP index around the earth at specific time#

datum = datetime(2015, 1, 10, 2, 30).replace(tzinfo=timezone.utc)
#datum = datetime(2016, 4, 21, 22,50,0).replace(tzinfo=timezone.utc)

df1 = globalIBP(datum)
df2 = globalIBP(datetime.now())

fig, ax = plt.subplots(2,1,figsize=(12, 8), layout='constrained')
df1.plot.scatter(x='Lon', y='IBP', title=f'IBP index at {datum.strftime("%Y-%m-%d %H:%M:%S")}', 
        xlabel='Longitude', ylabel='Probability', grid=True, xlim=(-180,179),
        c='IBP', colormap='plasma_r', vmin=0, vmax=1, ax=ax[0],
        sharex=False)
# sharex=False - must be there because there is still a bug and xlabel and tickmarks will not appear otherwise
df2.plot.scatter(x='Lon', y='IBP', title=f'IBP index now', 
        xlabel='Longitude', ylabel='Probability', grid=True, xlim=(-180,179),
        c='IBP', colormap='plasma_r', vmin=0, vmax=1, ax=ax[1],
        sharex=False)

for a in ax:
    a.set_ylim(top=1.05)

plt.show()
../_images/6287153ed85ed1ff78de42f57f9424988d8d3a5a11174f607185a1754df44ed6.png

IBP Index along the orbit of a satellite#

starttime = datetime(2022, 11, 26, 17,0,0).replace(tzinfo=timezone.utc)
endtime = datetime(2022, 11, 28, 5,0,0).replace(tzinfo=timezone.utc)

sat = 'B'

df = satelliteIBP(loadsatellitedata(starttime, endtime, sat))

display(df)

fig, axes = plt.subplots(1,2,figsize=(17, 5), layout='constrained')

df.plot(x="Longitude", y="Bubble_Index", ax=axes[0], color='orange', kind='scatter', label='IBI - Bubble_Index')
#df.plot(x="Longitude", y="Bubble_Probability", ax=axes[0], color='g', kind='scatter', label='IBI - Bubble_Probability')
df.plot(x="Longitude", y="IBP", kind="scatter", ax=axes[0], color='b', label='IBP')

df.plot(y="Bubble_Index", ax=axes[1], color='orange', marker=".", linewidth=0, label='IBI - Bubble_Index')
#df.plot(x="Loy="Bubble_Probability", ax=axes[0], color='g', kind='scatter', label='IBI - Bubble_Probability')
df.plot(y="IBP", marker=".", linewidth=0, ax=axes[1], color='b', label='IBP')


axes[0].set_xlabel('Longitude')
axes[0].set_xlim(-180,180)

for ax in axes:
    ax.set_ylabel('probability')
    ax.set_ylim(top=1.05)
    ax.legend()
    ax.set_title(f'Plasma Bubbles detectet by satellite {sat}')
    ax.grid()

plt.show() 
Spacecraft Flags_q Flags_B Longitude Radius Bubble_Probability Flags_Bubble F107 Bubble_Index Flags_F Latitude IBP
Timestamp
2022-11-26 17:32:38 B 0 0 41.735251 6888588.78 0.0 0 107.242806 0 1 -44.990729 NaN
2022-11-26 17:32:39 B 0 0 41.736026 6888584.63 0.0 0 107.242790 0 1 -44.927609 NaN
2022-11-26 17:32:40 B 0 0 41.736791 6888580.47 0.0 0 107.242774 0 1 -44.864488 NaN
2022-11-26 17:32:41 B 0 0 41.737544 6888576.30 0.0 0 107.242758 0 1 -44.801367 NaN
2022-11-26 17:32:42 B 0 0 41.738287 6888572.13 0.0 0 107.242742 0 1 -44.738245 NaN
... ... ... ... ... ... ... ... ... ... ... ... ...
2022-11-28 04:40:16 B 0 0 -122.568657 6875021.45 0.0 0 107.127674 0 1 44.727242 NaN
2022-11-28 04:40:17 B 0 0 -122.567896 6875010.38 0.0 0 107.127672 0 1 44.790612 NaN
2022-11-28 04:40:18 B 0 0 -122.567125 6874999.32 0.0 0 107.127669 0 1 44.853982 NaN
2022-11-28 04:40:19 B 0 0 -122.566342 6874988.26 0.0 0 107.127667 0 1 44.917352 NaN
2022-11-28 04:40:20 B 0 0 -122.565549 6874977.22 0.0 0 107.127665 0 1 44.980722 NaN

32710 rows × 12 columns

../_images/4d638f1ad7bd52cb4443e821fd5ea7448d5a535b97243239440c0c64c1c8bb83.png