Skip to content
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

Flexible way to fill DATAMAX/MIN BUNIT EXPOSURE in FITS header keywords #393

Merged
merged 15 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 11 additions & 23 deletions stixcore/io/fits/processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -695,13 +695,6 @@ def generate_primary_header(cls, filename, product, *, version=0):
# raise ValueError(f"Try to crate FITS file L0 for {product.level} data product")
# if not isinstance(product.obt_beg, SCETime):
# raise ValueError("Expected SCETime as time format")
dmin = 0.0
dmax = 0.0
bunit = ' '
if 'counts' in product.data.colnames:
dmax = product.data['counts'].max().value
dmin = product.data['counts'].min().value
bunit = 'counts'

headers = FitsProcessor.generate_common_header(filename, product, version=version) + (
# Name, Value, Comment
Expand All @@ -716,9 +709,9 @@ def generate_primary_header(cls, filename, product, *, version=0):
('DATE-BEG', product.scet_timerange.start.to_string(), 'Start time of observation'),
('DATE-AVG', product.scet_timerange.avg.to_string(), 'Average time of observation'),
('DATE-END', product.scet_timerange.end.to_string(), 'End time of observation'),
('DATAMIN', dmin, 'Minimum valid physical value'),
('DATAMAX', dmax, 'Maximum valid physical value'),
('BUNIT', bunit, 'Units of physical value, after application of BSCALE, BZERO')
('DATAMIN', product.dmin, 'Minimum valid physical value'),
('DATAMAX', product.dmax, 'Maximum valid physical value'),
('BUNIT', product.bunit, 'Units of physical value, after application of BSCALE, BZERO')
)

return headers
Expand All @@ -745,20 +738,11 @@ def generate_primary_header(self, filename, product, *, version=0):

headers = FitsProcessor.generate_common_header(filename, product, version=version)

dmin = 0.0
dmax = 0.0
bunit = ' '
exposure = 0.0
if 'counts' in product.data.colnames:
dmax = product.data['counts'].max().value
dmin = product.data['counts'].min().value
bunit = 'counts'
exposure = product.data['timedel'].as_float().min().to_value('s')
data_headers = (
('DATAMIN', dmin, 'Minimum valid physical value'),
('DATAMAX', dmax, 'Maximum valid physical value'),
('BUNIT', bunit, 'Units of physical value, after application of BSCALE, BZERO'),
('XPOSURE', exposure, '[s] shortest exposure time')
('DATAMIN', product.dmin, 'Minimum valid physical value'),
('DATAMAX', product.dmax, 'Maximum valid physical value'),
('BUNIT', product.bunit, 'Units of physical value, after application of BSCALE, BZERO'),
('XPOSURE', product.exposure, '[s] shortest exposure time')
)

soop_keywords = SOOPManager.instance.get_keywords(start=product.utc_timerange.start,
Expand Down Expand Up @@ -972,6 +956,10 @@ def generate_primary_header(self, filename, product, *, version=0):
('VERS_CFG', str(stixcore.__version_conf__),
'Version of the common instrument configuration package'),
('HISTORY', 'Processed by STIXCore L2'),
('DATAMIN', product.dmin, 'Minimum valid physical value'),
('DATAMAX', product.dmax, 'Maximum valid physical value'),
('BUNIT', product.bunit, 'Units of physical value, after application of BSCALE, BZERO'),
('XPOSURE', product.exposure, '[s] shortest exposure time')
)

return L1headers, L2headers
2 changes: 1 addition & 1 deletion stixcore/processing/find.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ def find_fits(args):
logger.info(f'#candidates: {n_candidates}')
logger.info(f'#found: {len(found)}')

for f in found:
for f in sorted(found):
print(str(f))
if args.copy_dest:
fits_parent_path = f.relative_to(fits_dir)
Expand Down
59 changes: 57 additions & 2 deletions stixcore/products/level0/quicklookL0.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@
rebin_proportional,
unscale_triggers,
)
from stixcore.products.product import Control, Data, EnergyChannelsMixin, GenericProduct
from stixcore.products.product import (
Control,
CountDataMixin,
Data,
EnergyChannelsMixin,
GenericProduct,
)
from stixcore.time import SCETime, SCETimeDelta, SCETimeRange
from stixcore.util.logging import get_logger

Expand All @@ -32,7 +38,7 @@
QLNIX00405_off = 0.1


class QLProduct(GenericProduct, EnergyChannelsMixin):
class QLProduct(CountDataMixin, GenericProduct, EnergyChannelsMixin):
"""Generic QL product class composed of control and data."""
def __init__(self, *, service_type, service_subtype, ssid, control, data,
idb_versions=defaultdict(SCETimeRange), **kwargs):
Expand Down Expand Up @@ -400,6 +406,14 @@ def from_levelb(cls, levelb, parent=''):
idb_versions=idb_versions,
packets=packets)

@property
def dmin(self):
return self.data['spectra'].min().value

@property
def dmax(self):
return self.data['spectra'].max().value

@classmethod
def _get_time(cls, control, num_energies, packets, pad_before, pad_after):
times = []
Expand Down Expand Up @@ -494,6 +508,19 @@ def from_levelb(cls, levelb, parent=''):
idb_versions=idb_versions,
packets=packets)

@property
def dmin(self):
return self.data['variance'].min()

@property
def dmax(self):
return self.data['variance'].max()

@property
def bunit(self):
# TODO define
return ' '

@classmethod
def is_datasource_for(cls, *, service_type, service_subtype, ssid, **kwargs):
return (kwargs['level'] == 'L0' and service_type == 21
Expand Down Expand Up @@ -554,6 +581,19 @@ def from_levelb(cls, levelb, parent=''):
idb_versions=idb_versions,
packets=packets)

@property
def dmin(self):
return min([self.data['loc_y'].min(), self.data['loc_z'].min()])

@property
def dmax(self):
return max([self.data['loc_y'].max(), self.data['loc_z'].max()])

@property
def bunit(self):
# TODO define
return ' '

@classmethod
def is_datasource_for(cls, *, service_type, service_subtype, ssid, **kwargs):
return (kwargs['level'] == 'L0' and service_type == 21
Expand Down Expand Up @@ -784,6 +824,21 @@ def from_levelb(cls, levelb, parent=''):
idb_versions=idb_versions,
packets=packets)

@property
def dmin(self):
# TODO define
return 0.0

@property
def dmax(self):
# TODO define
return 0.0

@property
def bunit(self):
# TODO define
return ''

@classmethod
def is_datasource_for(cls, *, service_type, service_subtype, ssid, **kwargs):
return (kwargs['level'] == 'L0' and service_type == 21
Expand Down
36 changes: 35 additions & 1 deletion stixcore/products/level0/scienceL0.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
)
from stixcore.products.product import (
ControlSci,
CountDataMixin,
Data,
EnergyChannelsMixin,
FitsHeaderMixin,
Expand Down Expand Up @@ -71,7 +72,7 @@ class NotCombineException(Exception):
pass


class ScienceProduct(GenericProduct, EnergyChannelsMixin, FitsHeaderMixin):
class ScienceProduct(CountDataMixin, GenericProduct, EnergyChannelsMixin, FitsHeaderMixin):
"""Generic science data product class composed of control and data."""
def __init__(self, *, service_type, service_subtype, ssid, control, data, **kwargs):
"""Create a generic science data product composed of control and data.
Expand Down Expand Up @@ -682,6 +683,21 @@ def from_levelb(cls, levelb, parent=''):
prod.add_additional_header_keywords(additional_header_keywords)
return prod

@property
def dmin(self):
# TODO define columns for dmin/max
return 0.0

@property
def dmax(self):
# TODO define columns for dmin/max
return 0.0

@property
def bunit(self):
# TODO define columns for dmin/max
return ' '

@classmethod
def is_datasource_for(cls, *, service_type, service_subtype, ssid, **kwargs):
return (kwargs['level'] == 'L0' and service_type == 21
Expand Down Expand Up @@ -938,6 +954,24 @@ def from_levelb(cls, levelb, parent=''):
idb_versions=idb_versions,
packets=packets)

@property
def dmin(self):
return min([self.data['cha_diode0'].min(),
self.data['cha_diode1'].min(),
self.data['chb_diode0'].min(),
self.data['chb_diode1'].min()])

@property
def dmax(self):
return max([self.data['cha_diode0'].max(),
self.data['cha_diode1'].max(),
self.data['chb_diode0'].max(),
self.data['chb_diode1'].max()])

@property
def bunit(self):
return ' '

@classmethod
def is_datasource_for(cls, *, service_type, service_subtype, ssid, **kwargs):
return (kwargs['level'] == 'L0' and service_type == 21
Expand Down
34 changes: 34 additions & 0 deletions stixcore/products/level1/quicklookL1.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ def __init__(self, *, service_type, service_subtype, ssid, control, data,
self.name = 'spectra'
self.level = 'L1'

@property
def dmin(self):
return self.data['spectra'].min().value

@property
def dmax(self):
return self.data['spectra'].max().value

@classmethod
def is_datasource_for(cls, *, service_type, service_subtype, ssid, **kwargs):
return (kwargs['level'] == 'L1' and service_type == 21
Expand All @@ -89,6 +97,19 @@ def __init__(self, *, service_type, service_subtype, ssid, control, data,
self.name = 'variance'
self.level = 'L1'

@property
def dmin(self):
return self.data['variance'].min()

@property
def dmax(self):
return self.data['variance'].max()

@property
def bunit(self):
# TODO define
return ' '

@classmethod
def is_datasource_for(cls, *, service_type, service_subtype, ssid, **kwargs):
return (kwargs['level'] == 'L1' and service_type == 21
Expand All @@ -109,6 +130,19 @@ def __init__(self, *, service_type, service_subtype, ssid, control, data,
self.name = 'flareflag'
self.level = 'L1'

@property
def dmin(self):
return min([self.data['loc_y'].min(), self.data['loc_z'].min()])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is value or to_value() not needed here as below?


@property
def dmax(self):
return max([self.data['loc_y'].max(), self.data['loc_z'].max()])

@property
def bunit(self):
# TODO define
return ' '

@classmethod
def is_datasource_for(cls, *, service_type, service_subtype, ssid, **kwargs):
return (kwargs['level'] == 'L1' and service_type == 21
Expand Down
33 changes: 33 additions & 0 deletions stixcore/products/level1/scienceL1.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,21 @@ def __init__(self, *, service_type, service_subtype, ssid, control,
self.name = 'xray-vis'
self.level = 'L1'

@property
def dmin(self):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea this might be tricky as they are complex numbers

# TODO define columns for dmin/max
return 0.0

@property
def dmax(self):
# TODO define columns for dmin/max
return 0.0

@property
def bunit(self):
# TODO define columns for dmin/max
return ' '

@classmethod
def is_datasource_for(cls, *, service_type, service_subtype, ssid, **kwargs):
return (kwargs['level'] == 'L1' and service_type == 21
Expand Down Expand Up @@ -113,6 +128,24 @@ def __init__(self, *, service_type, service_subtype, ssid, control,
self.name = 'aspect-burst'
self.level = 'L1'

@property
def dmin(self):
return min([self.data['cha_diode0'].min(),
self.data['cha_diode1'].min(),
self.data['chb_diode0'].min(),
self.data['chb_diode1'].min()])

@property
def dmax(self):
return max([self.data['cha_diode0'].max(),
self.data['cha_diode1'].max(),
self.data['chb_diode0'].max(),
self.data['chb_diode1'].max()])

@property
def bunit(self):
return ' '

@classmethod
def is_datasource_for(cls, *, service_type, service_subtype, ssid, **kwargs):
return (kwargs['level'] == 'L1' and service_type == 21
Expand Down
12 changes: 12 additions & 0 deletions stixcore/products/level2/housekeepingL2.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,18 @@ def __init__(self, *, service_type=0, service_subtype=0, ssid=1, control, data,
self.service_subtype = 0
self.service_type = 0

@property
def dmin(self):
return min([self.data['y_srf'].min().value, self.data['z_srf'].min().value])
nicHoch marked this conversation as resolved.
Show resolved Hide resolved

@property
def dmax(self):
return max([self.data['y_srf'].max().value, self.data['z_srf'].max().value])

@property
def bunit(self):
return 'arcsec'

@classmethod
def is_datasource_for(cls, *, service_type, service_subtype, ssid, **kwargs):
return (kwargs['level'] == 'L2' and service_type == 0
Expand Down
Loading
Loading