-
Notifications
You must be signed in to change notification settings - Fork 22
Skip L2 processing if no good times are found #2775
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
5be74c2
334b50b
9093860
7bfb489
ecf3753
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,7 @@ | ||
| """Module containing the class definition for the HistogramL2 class.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from dataclasses import InitVar, dataclass, field | ||
|
|
||
| import numpy as np | ||
|
|
@@ -123,10 +125,8 @@ class HistogramL2: | |
|
|
||
| Parameters | ||
| ---------- | ||
| l1b_dataset : xr.Dataset | ||
| GLOWS histogram L1B dataset, as produced by glows_l1b.py. | ||
| pipeline_settings : PipelineSettings | ||
| Pipeline settings object read from ancillary file. | ||
| good_l1b_data : xr.Dataset | ||
| GLOWS histogram L1B dataset filtered by good times. | ||
|
|
||
| Attributes | ||
| ---------- | ||
|
|
@@ -210,123 +210,142 @@ class HistogramL2: | |
| spin_axis_orientation_average: np.ndarray[np.double] | ||
| bad_time_flag_occurrences: np.ndarray | ||
|
|
||
| def __init__(self, l1b_dataset: xr.Dataset, pipeline_settings: PipelineSettings): | ||
| def __init__(self, good_l1b_data: xr.Dataset): | ||
| """ | ||
| Given an L1B dataset, process data into an output HistogramL2 object. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| l1b_dataset : xr.Dataset | ||
| GLOWS histogram L1B dataset, as produced by glows_l1b.py. | ||
| pipeline_settings : PipelineSettings | ||
| Pipeline settings object read from ancillary file. | ||
| good_l1b_data : xr.Dataset | ||
| GLOWS histogram L1B dataset filtered by good times. | ||
| """ | ||
| active_flags = np.array(pipeline_settings.active_bad_time_flags, dtype=float) | ||
|
|
||
| # Select the good blocks (i.e. epoch values) according to the flags. Drop any | ||
| # bad blocks before processing. | ||
| good_data = l1b_dataset.isel( | ||
| epoch=self.return_good_times(l1b_dataset["flags"], active_flags) | ||
| ) | ||
| # todo: bad angle filter | ||
| # TODO: bad angle filter | ||
| # TODO filter bad bins out. Needs to happen here while everything is still | ||
| # per-timestamp. | ||
| # per-timestamp. | ||
|
|
||
| self.daily_lightcurve = DailyLightcurve(good_data) | ||
| self.daily_lightcurve = DailyLightcurve(good_l1b_data) | ||
|
|
||
| self.total_l1b_inputs = len(good_data["epoch"]) | ||
| self.number_of_good_l1b_inputs = len(good_data["epoch"]) | ||
| self.total_l1b_inputs = len(good_l1b_data["epoch"]) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not correct - this variable should be ALL the L1B inputs, including bad times.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The original variable passed in was "good_data" which only included the good times. This change is only renaming that variable. I'm not sure I follow |
||
| self.number_of_good_l1b_inputs = len(good_l1b_data["epoch"]) | ||
| self.identifier = -1 # TODO: retrieve from spin table | ||
| # TODO fill this in | ||
| self.bad_time_flag_occurrences = np.zeros((1, FLAG_LENGTH)) | ||
|
|
||
| if len(good_data["epoch"]) != 0: | ||
| # Generate outputs that are passed in directly from L1B | ||
| self.start_time = good_data["epoch"].data[0] | ||
| self.end_time = good_data["epoch"].data[-1] | ||
| else: | ||
| # No good times in the file | ||
| self.start_time = l1b_dataset["imap_start_time"].data[0] | ||
| self.end_time = ( | ||
| l1b_dataset["imap_start_time"].data[0] | ||
| + l1b_dataset["imap_time_offset"].data[0] | ||
| ) | ||
| # Generate outputs that are passed in directly from L1B | ||
| self.start_time = good_l1b_data["epoch"].data[0] | ||
| self.end_time = good_l1b_data["epoch"].data[-1] | ||
|
|
||
| self.filter_temperature_average = ( | ||
| good_data["filter_temperature_average"] | ||
| good_l1b_data["filter_temperature_average"] | ||
| .mean(dim="epoch", keepdims=True) | ||
| .data | ||
| ) | ||
| self.filter_temperature_std_dev = ( | ||
| good_data["filter_temperature_average"].std(dim="epoch", keepdims=True).data | ||
| good_l1b_data["filter_temperature_average"] | ||
| .std(dim="epoch", keepdims=True) | ||
| .data | ||
| ) | ||
| self.hv_voltage_average = ( | ||
| good_data["hv_voltage_average"].mean(dim="epoch", keepdims=True).data | ||
| good_l1b_data["hv_voltage_average"].mean(dim="epoch", keepdims=True).data | ||
| ) | ||
| self.hv_voltage_std_dev = ( | ||
| good_data["hv_voltage_average"].std(dim="epoch", keepdims=True).data | ||
| good_l1b_data["hv_voltage_average"].std(dim="epoch", keepdims=True).data | ||
| ) | ||
| self.spin_period_average = ( | ||
| good_data["spin_period_average"].mean(dim="epoch", keepdims=True).data | ||
| good_l1b_data["spin_period_average"].mean(dim="epoch", keepdims=True).data | ||
| ) | ||
| self.spin_period_std_dev = ( | ||
| good_data["spin_period_average"].std(dim="epoch", keepdims=True).data | ||
| good_l1b_data["spin_period_average"].std(dim="epoch", keepdims=True).data | ||
| ) | ||
| self.pulse_length_average = ( | ||
| good_data["pulse_length_average"].mean(dim="epoch", keepdims=True).data | ||
| good_l1b_data["pulse_length_average"].mean(dim="epoch", keepdims=True).data | ||
| ) | ||
| self.pulse_length_std_dev = ( | ||
| good_data["pulse_length_average"].std(dim="epoch", keepdims=True).data | ||
| good_l1b_data["pulse_length_average"].std(dim="epoch", keepdims=True).data | ||
| ) | ||
| self.spin_period_ground_average = ( | ||
| good_data["spin_period_ground_average"] | ||
| good_l1b_data["spin_period_ground_average"] | ||
| .mean(dim="epoch", keepdims=True) | ||
| .data | ||
| ) | ||
| self.spin_period_ground_std_dev = ( | ||
| good_data["spin_period_ground_average"].std(dim="epoch", keepdims=True).data | ||
| good_l1b_data["spin_period_ground_average"] | ||
| .std(dim="epoch", keepdims=True) | ||
| .data | ||
| ) | ||
| self.position_angle_offset_average = ( | ||
| good_data["position_angle_offset_average"] | ||
| good_l1b_data["position_angle_offset_average"] | ||
| .mean(dim="epoch", keepdims=True) | ||
| .data | ||
| ) | ||
| self.position_angle_offset_std_dev = ( | ||
| good_data["position_angle_offset_average"] | ||
| good_l1b_data["position_angle_offset_average"] | ||
| .std(dim="epoch", keepdims=True) | ||
| .data | ||
| ) | ||
| self.spacecraft_location_average = ( | ||
| good_data["spacecraft_location_average"] | ||
| good_l1b_data["spacecraft_location_average"] | ||
| .mean(dim="epoch") | ||
| .data[np.newaxis, :] | ||
| ) | ||
| self.spacecraft_location_std_dev = ( | ||
| good_data["spacecraft_location_average"] | ||
| good_l1b_data["spacecraft_location_average"] | ||
| .std(dim="epoch") | ||
| .data[np.newaxis, :] | ||
| ) | ||
| self.spacecraft_velocity_average = ( | ||
| good_data["spacecraft_velocity_average"] | ||
| good_l1b_data["spacecraft_velocity_average"] | ||
| .mean(dim="epoch") | ||
| .data[np.newaxis, :] | ||
| ) | ||
| self.spacecraft_velocity_std_dev = ( | ||
| good_data["spacecraft_velocity_average"] | ||
| good_l1b_data["spacecraft_velocity_average"] | ||
| .std(dim="epoch") | ||
| .data[np.newaxis, :] | ||
| ) | ||
| self.spin_axis_orientation_average = ( | ||
| good_data["spin_axis_orientation_average"] | ||
| good_l1b_data["spin_axis_orientation_average"] | ||
| .mean(dim="epoch") | ||
| .data[np.newaxis, :] | ||
| ) | ||
| self.spin_axis_orientation_std_dev = ( | ||
| good_data["spin_axis_orientation_average"] | ||
| good_l1b_data["spin_axis_orientation_average"] | ||
| .std(dim="epoch") | ||
| .data[np.newaxis, :] | ||
| ) | ||
|
|
||
| @classmethod | ||
| def create( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This duplicates the current init function and makes it confusing IMO (this makes L2 behave differently from the other levels.) Instead of making this a class function, why not make it a static function that can then be used at the beginning of init? Or just leaving it inside init?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wasn't able to return None from init which is why I used a factory pattern here. To avoid returning dataset class objects that have bad data, I was trying to cut the processing early. I could raise an exception instead and catch it in I was also trying to keep processing steps in the dataclass file to maintain the separation of concerns in the two L2 files. I interpreted |
||
| cls, l1b_dataset: xr.Dataset, pipeline_settings: PipelineSettings | ||
| ) -> HistogramL2 | None: | ||
| """ | ||
| Create a HistogramL2 object from an L1B dataset, filtering out bad times. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| l1b_dataset : xr.Dataset | ||
| GLOWS histogram L1B dataset, as produced by glows_l1b.py. | ||
| pipeline_settings : PipelineSettings | ||
| Pipeline settings object read from ancillary file. | ||
|
|
||
| Returns | ||
| ------- | ||
| HistogramL2 or None | ||
| A HistogramL2 object if there are good times in the dataset, | ||
| or None if there are no good times. | ||
| """ | ||
| # Check if dataset contains good times | ||
| good_idx = cls.return_good_times( | ||
| l1b_dataset["flags"], | ||
| np.array(pipeline_settings.active_bad_time_flags, dtype=float), | ||
| ) | ||
| if len(good_idx) == 0: | ||
| return None | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is confusing to me that a class creation step could return
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Filtering could certainly occur before class creation. I was trying to keep all processing steps out of the glows_l2.py file but if that's not important this is an easy change. I think filtering first then creating the object is better than creating a bad object since those steps aren't necessary even if they're fast |
||
|
|
||
| # Proceed with creating the HistogramL2 object using only the good data | ||
| filtered_data = l1b_dataset.isel(epoch=good_idx) | ||
| return cls(filtered_data) | ||
|
|
||
| def filter_bad_bins(self, histograms: NDArray, bin_exclusions: NDArray) -> NDArray: | ||
| """ | ||
| Filter out bad bins from the histogram. | ||
|
|
@@ -372,7 +391,7 @@ def return_good_times(flags: xr.DataArray, active_flags: NDArray) -> NDArray: | |
| An array of indices for good times. | ||
| """ | ||
| if len(active_flags) != flags.shape[1]: | ||
| print("Active flags don't matched expected length") | ||
| print("Active flags don't match expected length") | ||
|
|
||
| # A good time is where all the active flags are equal to one. | ||
| # Here, we mask the active indices using active_flags, and then return the times | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PipelineSettings has other data in it that needs to be used for future L2 functionality, so it still needs to be included in this init
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't know about future L2 functionality. Can you expand on that?