Skip to content

Commit

Permalink
Merge pull request #33 from Christian-Palmroos/main
Browse files Browse the repository at this point in the history
Added SolO/STEP compatibility
  • Loading branch information
jgieseler authored Apr 18, 2023
2 parents 0cd3048 + 2098c01 commit 85283fe
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 33 deletions.
155 changes: 124 additions & 31 deletions seppy/tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,16 +139,29 @@ def load_data(self, spacecraft, sensor, viewing, data_level,
autodownload=True, threshold=None):

if self.spacecraft == 'solo':
df_i, df_e, energs = epd_load(sensor=sensor,
viewing=viewing,
level=data_level,
startdate=self.start_date,
enddate=self.end_date,
path=self.data_path,
autodownload=autodownload)

self.update_viewing(viewing)
return df_i, df_e, energs
if self.sensor in ("ept", "het"):
df_i, df_e, meta = epd_load(sensor=sensor,
viewing=viewing,
level=data_level,
startdate=self.start_date,
enddate=self.end_date,
path=self.data_path,
autodownload=autodownload)
self.update_viewing(viewing)
return df_i, df_e, meta

elif self.sensor == "step":
df, meta = epd_load(sensor=sensor,
viewing="None",
level=data_level,
startdate=self.start_date,
enddate=self.end_date,
path=self.data_path,
autodownload=autodownload)

self.update_viewing(viewing)
return df, meta

if self.spacecraft[:2].lower() == 'st':
if self.sensor == 'sept':
Expand Down Expand Up @@ -336,7 +349,7 @@ def load_all_viewing(self):
elif self.sensor == 'step':

self.df_step, self.energies_step =\
self.load_data(self.spacecraft, self.sensor, 'None',
self.load_data(self.spacecraft, self.sensor, self.viewing,
self.data_level)

if self.spacecraft[:2].lower() == 'st':
Expand Down Expand Up @@ -460,6 +473,23 @@ def choose_data(self, viewing):
self.current_df_i = self.df_i_south
self.current_df_e = self.df_e_south
self.current_energies = self.energies_south

elif "Pixel" in viewing:

# All viewings are contained in the same dataframe, choose the pixel (viewing) here
pixel = self.viewing.split(' ')[1]

# Pixel info is in format NN in the dataframe, 1 -> 01 while 12 -> 12
if len(pixel) == 1:
pixel = f"0{pixel}"

# Pixel length more than 2 means "averaged" -> called "Avg" in the dataframe
elif len(pixel) > 2:
pixel = "Avg"

self.current_df_i = self.df_step[[ col for col in self.df_step.columns if f"Magnet_{pixel}_Flux" in col]]
self.current_df_e = self.df_step[[ col for col in self.df_step.columns if f"Electron_{pixel}_Flux" in col]]
self.current_energies = self.energies_step

if self.spacecraft[:2].lower() == 'st':
if self.sensor == 'sept':
Expand Down Expand Up @@ -1131,6 +1161,7 @@ def find_onset(self, viewing, bg_start=None, bg_length=None, background_range=No

if (self.spacecraft[:2].lower() == 'st' and self.sensor == 'sept') \
or (self.spacecraft.lower() == 'psp' and self.sensor.startswith('isois')) \
or (self.spacecraft.lower() == 'solo' and self.sensor == 'step') \
or (self.spacecraft.lower() == 'solo' and self.sensor == 'ept') \
or (self.spacecraft.lower() == 'solo' and self.sensor == 'het') \
or (self.spacecraft.lower() == 'wind' and self.sensor == '3dp') \
Expand Down Expand Up @@ -1179,6 +1210,26 @@ def find_onset(self, viewing, bg_start=None, bg_length=None, background_range=No
self.current_energies,
channels)

elif self.sensor == "step":

if len(channels) > 1:
not_implemented_msg = "Multiple channel averaging not yet supported for STEP! Please choose only one channel."
raise Exception(not_implemented_msg)

en_channel_string = self.get_channel_energy_values("str")[channels[0]]

if self.species in ('p', 'i'):
channel_id = self.current_df_i.columns[channels[0]]
df_flux = pd.DataFrame(data={
"flux" : self.current_df_i[channel_id]
}, index = self.current_df_i.index)

elif self.species == 'e':
channel_id = self.current_df_e.columns[channels[0]]
df_flux = pd.DataFrame(data={
"flux" : self.current_df_e[channel_id]
}, index = self.current_df_e.index)

else:
invalid_sensor_msg = "Invalid sensor!"
raise Exception(invalid_sensor_msg)
Expand Down Expand Up @@ -1389,10 +1440,35 @@ def dynamic_spectrum(self, view, cmap: str = 'magma', xlim: tuple = None, resamp
instrument = self.sensor.lower()
species = self.species

# This method has to be run before doing anything else to make sure that the viewing is correct
self.choose_data(view)

if self.spacecraft == "solo":
if species in ("electron", 'e'):

if instrument == "step":

# All viewings are contained in the same dataframe, choose the pixel (viewing) here
pixel = self.viewing.split(' ')[1]

# Pixel info is in format NN in the dataframe, 1 -> 01 while 12 -> 12
if len(pixel) == 1:
pixel = f"0{pixel}"

# Pixel length more than 2 means "averaged" -> called "Avg" in the dataframe
elif len(pixel) > 2:
pixel = "Avg"

if species in ("electron", 'e'):
particle_data = self.df_step[[ col for col in self.df_step.columns if f"Electron_{pixel}_Flux" in col]]
s_identifier = "electrons"

# Step's "Magnet" channel deflects electrons -> measures all positive ions
else:
particle_data = self.df_step[[ col for col in self.df_step.columns if f"Magnet_{pixel}_Flux" in col]]
s_identifier = "ions"

# EPT and HET data come in almost identical containers, they need not be differentiated
elif species in ("electron", 'e'):
particle_data = self.current_df_e["Electron_Flux"]
s_identifier = "electrons"
else:
Expand Down Expand Up @@ -1452,7 +1528,7 @@ def dynamic_spectrum(self, view, cmap: str = 'magma', xlim: tuple = None, resamp
s_identifier = "protons"

# These particle instruments will have keVs on their y-axis
LOW_ENERGY_SENSORS = ("sept", "ept")
LOW_ENERGY_SENSORS = ("sept", "step", "ept")

if instrument in LOW_ENERGY_SENSORS:
y_multiplier = 1e-3 # keV
Expand All @@ -1469,11 +1545,11 @@ def dynamic_spectrum(self, view, cmap: str = 'magma', xlim: tuple = None, resamp
df = particle_data[:]
t_start, t_end = df.index[0], df.index[-1]
else:
# td is added to the end to avert white pixels at the end of the plot
# td is added to the start and the end to avert white pixels at the end of the plot
td_str = resample if resample is not None else '0s'
td = pd.Timedelta(value=td_str)
t_start, t_end = pd.to_datetime(xlim[0]), pd.to_datetime(xlim[1])
df = particle_data.loc[(particle_data.index >= t_start) & (particle_data.index <= (t_end+td))]
df = particle_data.loc[(particle_data.index >= (t_start-td)) & (particle_data.index <= (t_end+td))]

# In practice this seeks the date on which the highest flux is observed
date_of_event = df.iloc[np.argmax(df[df.columns[0]])].name.date()
Expand Down Expand Up @@ -1640,20 +1716,32 @@ def tsa_plot(self, view, selection=None, xlim=None, resample=None):
self.choose_data(view)

if self.spacecraft == "solo":
if species in ["electron", 'e']:
particle_data = self.current_df_e["Electron_Flux"]
s_identifier = "electrons"
else:
try:
particle_data = self.current_df_i["Ion_Flux"]
if self.sensor == "step":

if species in ("electron", 'e'):
particle_data = self.current_df_e
s_identifier = "electrons"
else:
particle_data = self.current_df_i
s_identifier = "ions"
except KeyError:
particle_data = self.current_df_i["H_Flux"]
s_identifier = "protons"

else:

if species in ("electron", 'e'):
particle_data = self.current_df_e["Electron_Flux"]
s_identifier = "electrons"
else:
try:
particle_data = self.current_df_i["Ion_Flux"]
s_identifier = "ions"
except KeyError:
particle_data = self.current_df_i["H_Flux"]
s_identifier = "protons"

sc_identifier = "Solar Orbiter"

if self.spacecraft[:2] == "st":
if species in ["electron", 'e']:
if species in ("electron", 'e'):
if instrument == "sept":
particle_data = self.current_df_e[[ch for ch in self.current_df_e.columns if ch[:2] == "ch"]]
else:
Expand Down Expand Up @@ -1870,7 +1958,7 @@ def timeshift(sliderobject):
line.set_xdata(line.get_xdata() - pd.Timedelta(seconds=timedelta_sec))

# Update the path label artist
text.set_text(f"R={radial_distance_value:.2f} AU\nL = {slider.value} AU")
text.set_text(f"R={radial_distance_value:.2f} AU\nL = {np.round(slider.value,2)} AU")

# Effectively this refreshes the figure
fig.canvas.draw_idle()
Expand Down Expand Up @@ -1920,16 +2008,15 @@ def get_channel_energy_values(self, returns: str = "num") -> list:
# First check by spacecraft, then by sensor
if self.spacecraft == "solo":

# All solo energies are in the same object
# STEP, ETP and HET energies are in the same object
energy_dict = self.current_energies

if self.species == 'e':
energy_ranges = energy_dict["Electron_Bins_Text"]

else:
try:
energy_ranges = energy_dict["Ion_Bins_Text"]
except KeyError:
energy_ranges = energy_dict["H_Bins_Text"]
p_identifier = "Ion_Bins_Text" if self.sensor == "ept" else "H_Bins_Text" if self.sensor == "het" else "Bins_Text"
energy_ranges = energy_dict[p_identifier]

# Each element in the list is also a list with len==1, so fix that
energy_ranges = [element[0] for element in energy_ranges]
Expand Down Expand Up @@ -1997,7 +2084,7 @@ def get_channel_energy_values(self, returns: str = "num") -> list:
energies_high_rounded = np.round(energies_high, 1)

# I think nan values should be removed at this point. However, if we were to do that, then print_energies()
# will not work anymore since tha number of channels and channel energy ranges won't be the same.
# will not work anymore since the number of channels and channel energy ranges won't be the same.
# In the current state PSP/ISOIS-EPILO cannot be examined with dynamic_spectrum(), because there are nan values
# in the channel energy ranges.
# energies_low_rounded = np.array([val for val in energies_low_rounded if not np.isnan(val)])
Expand Down Expand Up @@ -2141,9 +2228,15 @@ def print_energies(self):

# Extract only the numbers from channel names
if self.spacecraft == "solo":

if self.sensor == "step":
channel_names = list(channel_names)
channel_numbers = np.array([int(name.split('_')[-1]) for name in channel_names])

if self.sensor == "ept":
channel_names = [name[1] for name in channel_names[:SOLO_EPT_CHANNELS_AMOUNT]]
channel_numbers = np.array([int(name.split('_')[-1]) for name in channel_names])

if self.sensor == "het":
channel_names = [name[1] for name in channel_names[:SOLO_HET_CHANNELS_AMOUNT]]
channel_numbers = np.array([int(name.split('_')[-1]) for name in channel_names])
Expand Down
7 changes: 5 additions & 2 deletions seppy/tools/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
A library to run the interactive user interface in SEP event onset determination notebooks.
@Author: Christian Palmroos <[email protected]>
@Last updated: 2022-11-03
@Last updated: 2023-03-24
"""


Expand All @@ -13,7 +13,7 @@
list_of_sc = ["PSP", "SOHO", "Solar Orbiter", "STEREO-A", "STEREO-B", "Wind"]

stereo_instr = ["SEPT", "HET"] # ["LET", "SEPT", "HET"]
solo_instr = ["EPT", "HET"]
solo_instr = ["STEP", "EPT", "HET"]
bepi_instr = ["SIXS-P"]
soho_instr = ["ERNE-HED", "EPHIN"]
psp_instr = ["isois-epihi", "isois-epilo"]
Expand All @@ -32,6 +32,8 @@
view_dict = {
("STEREO-A", "SEPT"): ("sun", "asun", "north", "south"),
("STEREO-B", "SEPT"): ("sun", "asun", "north", "south"),
("Solar Orbiter", "STEP"): ("Pixel averaged", "Pixel 1", "Pixel 2", "Pixel 3", "Pixel 4", "Pixel 5", "Pixel 6", "Pixel 7", "Pixel 8", "Pixel 9", "Pixel 10",
"Pixel 11", "Pixel 12", "Pixel 13", "Pixel 14", "Pixel 15"),
("Solar Orbiter", "EPT"): ("sun", "asun", "north", "south"),
("Solar Orbiter", "HET"): ("sun", "asun", "north", "south"),
("Bepicolombo", "SIXS-P"): (0, 1, 2, 3, 4),
Expand All @@ -47,6 +49,7 @@
("STEREO-B", "LET"): ("protons", "electrons"),
("STEREO-B", "SEPT"): ("ions", "electrons"),
("STEREO-B", "HET"): ("protons", "electrons"),
("Solar Orbiter", "STEP"): ("ions", "electrons"),
("Solar Orbiter", "EPT"): ("ions", "electrons"),
("Solar Orbiter", "HET"): ("protons", "electrons"),
("Bepicolombo", "SIXS-P"): ("protons", "electrons"),
Expand Down

0 comments on commit 85283fe

Please sign in to comment.