Skip to content

Commit

Permalink
Detector detection
Browse files Browse the repository at this point in the history
  • Loading branch information
sesquideus committed Dec 10, 2024
1 parent 0ed032c commit adf9105
Show file tree
Hide file tree
Showing 9 changed files with 50 additions and 44 deletions.
14 changes: 11 additions & 3 deletions metisp/pymetis/src/pymetis/base/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,21 @@ def output_file_name(self) -> str:
return f"{self.category}.fits"


class DetectorProduct(PipelineProduct, ABC):
class DetectorSpecificProduct(PipelineProduct, ABC):
detector = None

def __init__(self,
recipe: 'MetisRecipe',
header: cpl.core.PropertyList,
image: cpl.core.Image,
*,
detector: str,
detector: str = None,
**kwargs):
self.detector = detector

if detector is not None:
self.detector = detector

if self.detector is None:
raise NotImplementedError("Products specific to a detector must define 'detector'")

super().__init__(recipe, header, image, **kwargs)
14 changes: 10 additions & 4 deletions metisp/pymetis/src/pymetis/inputs/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class PipelineInput:
_required: bool = True # By default, inputs are required to be present
_tags: Pattern = None # No universal tags are provided
_group: str = None # No sensible default, must be provided explicitly
_detector: str = None # No default

@property
def title(self):
Expand All @@ -48,6 +49,10 @@ def required(self):
def group(self):
return self._group

@property
def detector(self):
return self._detector

def __init__(self,
*,
title: str = None,
Expand Down Expand Up @@ -97,7 +102,7 @@ def print_debug(self, *, offset: int = 0) -> None:
"""
Print a short description of the tags with a small offset (spaces).
"""
Msg.debug(self.__class__.__qualname__, f"{' ' * offset}Tags: {self.tags}")
Msg.debug(self.__class__.__qualname__, f"{' ' * offset}Tag: {self.tags}")


class SinglePipelineInput(PipelineInput):
Expand Down Expand Up @@ -190,7 +195,7 @@ def _verify_frameset_not_empty(self) -> None:
else:
Msg.debug(self.__class__.__qualname__, f"No {self.title} frames found but not required.")
else:
Msg.debug(self.__class__.__qualname__, f"OK: {count} frames found")
Msg.debug(self.__class__.__qualname__, f"Frameset OK: {count} frame{'s' if count > 1 else ''} found")

def _verify_same_detector(self) -> None:
"""
Expand Down Expand Up @@ -224,10 +229,11 @@ def _verify_same_detector(self) -> None:
except KeyError:
Msg.warning(self.__class__.__qualname__, f"No detector (ESO DPR TECH) set!")


# Check if all the raws have the same detector, if not, we have a problem
if len(unique := list(set(detectors))) == 1:
self._detector_name = unique[0]
self._detector = unique[0]
Msg.debug(self.__class__.__qualname__,
f"Detector determined: {self.detector}")
elif len(unique) == 0:
Msg.warning(self.__class__.__qualname__,
f"No detectors specified (this is probably fine in skeleton stage)")
Expand Down
16 changes: 16 additions & 0 deletions metisp/pymetis/src/pymetis/inputs/inputset.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class PipelineInputSet(metaclass=ABCMeta):
"""

inputs: [PipelineInput] = []
detector: str = None

def __init__(self, frameset: cpl.ui.FrameSet, **kwargs):
""" Filter the input frameset, capture frames that match criteria and assign them to own attributes. """
Expand All @@ -53,6 +54,21 @@ def verify(self) -> None:
for inp in self.inputs:
inp.verify()

self.verify_detectors()

def verify_detectors(self) -> None:
"""
Verify that the provided SOF contains frames from only a single detector.
Some Inputs have None if they are not specific to a detector.
"""
detectors = list(set([inp.detector for inp in self.inputs]) - {None})
if (detector_count := len(detectors)) != 1:
Msg.debug(self.__class__.__qualname__, f"No detector could be identified from the SOF")
elif detector_count == 1:
self.detector = detectors[0]
else:
raise ValueError(f"More than one detector found in inputset: {detectors}")

def print_debug(self, *, offset: int = 0) -> None:
Msg.debug(self.__class__.__qualname__, f"{' ' * offset} -- Detailed class info ---")
Msg.debug(self.__class__.__qualname__, f"{' ' * offset}{len(self.inputs)} inputs:")
Expand Down
15 changes: 3 additions & 12 deletions metisp/pymetis/src/pymetis/mixins/gainmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
import re

import cpl

from pymetis.base.input import RecipeInput


class GainMapInputMixin(RecipeInput):
tags_gain_map = [] # ["GAIN_MAP_det"]
tags_gain_map = re.compile(r"GAIN_MAP_(?P<detector>2RG|GEO|IFU)")

def __init__(self, frameset: cpl.ui.FrameSet, **kwargs):
self.gain_map: cpl.core.Image | None = None
Expand All @@ -45,14 +46,4 @@ def categorize_frame(self, frame: cpl.ui.Frame) -> None:

def verify(self) -> None:
self._verify_frame_present(self.gain_map, "gain map")
super().verify()


class GainMap2rgInputMixin(GainMapInputMixin):
""" A gain map for the IFU """
tags_gain_map = ["GAIN_MAP_2RG"]


class GainMapGeoInputMixin(GainMapInputMixin):
""" A gain map for the N band """
tags_gain_map = ["GAIN_MAP_GEO"]
super().verify()
2 changes: 1 addition & 1 deletion metisp/pymetis/src/pymetis/prefab/darkimage.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,5 @@ class InputSet(RawImageProcessor.InputSet):

def __init__(self, frameset: cpl.ui.FrameSet):
super().__init__(frameset)
self.master_dark = self.MasterDarkInput(frameset, det=self.detector)
self.master_dark = self.MasterDarkInput(frameset)
self.inputs += [self.master_dark]
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class Raw(RawInput):
class MasterFlat(MasterFlatInput):
_tags = re.compile(r"MASTER_IMG_FLAT_(?P<source>LAMP|TWILIGHT)_(?P<band>LM)")

# We could define the master dark explicitly too, but we can use a prefabricated class instead.
# We could define the master dark explicitly too, but we can use a whole prefabricated class instead.
# That already has its tags defined (for master darks it's always "MASTER_DARK_{det}"), so we just define
# the detector and band. Those are now available for all Input classes here.
# Of course, we could be more explicit and define them directly.
Expand All @@ -89,7 +89,7 @@ class Product(PipelineProduct):
so its name is `Product` (or fully qualified, `MetisLmImgBasicReduceImpl.Product`).
But feel free to be more creative with names.
"""
tag: str = "LM_{target}_REDUCED"
tag: str = "LM_{self.target}_REDUCED"
group = cpl.ui.Frame.FrameGroup.PRODUCT
level = cpl.ui.Frame.FrameLevel.FINAL
frame_type = cpl.ui.Frame.FrameType.IMAGE
Expand Down Expand Up @@ -123,9 +123,9 @@ def prepare_images(self,
prepared_images = cpl.core.ImageList()

for index, frame in enumerate(raw_frames):
Msg.info(self.__class__.__qualname__, f"Processing {frame.file!r}...")
Msg.info(self.__class__.__qualname__, f"Processing {frame.file}...")

Msg.debug(self.__class__.__qualname__, f"Loading image {frame.file!r}")
Msg.debug(self.__class__.__qualname__, f"Loading image {frame.file}")
raw_image = cpl.core.Image.load(frame.file, extension=1)

if bias:
Expand Down
11 changes: 5 additions & 6 deletions metisp/pymetis/src/pymetis/recipes/metis_det_lingain.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@
import cpl

from pymetis.base import MetisRecipe
from pymetis.base.product import PipelineProduct, DetectorProduct
from pymetis.base.product import PipelineProduct, DetectorSpecificProduct
from pymetis.inputs.common import RawInput
from pymetis.prefab.rawimage import RawImageProcessor
from pymetis.prefab.darkimage import DarkImageProcessor


class LinGainProduct(DetectorProduct, ABC):
class LinGainProduct(DetectorSpecificProduct, ABC):
group = cpl.ui.Frame.FrameGroup.PRODUCT
level = cpl.ui.Frame.FrameLevel.FINAL
frame_type = cpl.ui.Frame.FrameType.IMAGE
Expand All @@ -53,17 +53,17 @@ def __init__(self, frameset: cpl.ui.FrameSet):
class ProductGain(LinGainProduct):
@property
def category(self) -> str:
return f"GAIN_MAP_{self.detector}"
return f"GAIN_MAP_{self.detector:s}"

class ProductLinearity(LinGainProduct):
@property
def category(self) -> str:
return f"LINEARITY_{self.detector}"
return f"LINEARITY_{self.detector:s}"

class ProductBadpixMap(LinGainProduct):
@property
def category(self) -> str:
return f"BADPIX_MAP_{self.detector}"
return f"BADPIX_MAP_{self.detector:s}"

def process_images(self) -> Dict[str, PipelineProduct]:
raw_images = self.load_raw_images()
Expand All @@ -83,7 +83,6 @@ def process_images(self) -> Dict[str, PipelineProduct]:
linearity_image = combined_image # TODO Actual implementation missing
badpix_map = combined_image # TODO Actual implementation missing

#import pdb ; pdb.set_trace()
self.products = {
f'MASTER_GAIN_{self.detector_name}':
self.ProductGain(self, header, gain_image,
Expand Down
2 changes: 0 additions & 2 deletions metisp/pymetis/src/pymetis/tests/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
from abc import ABC
from pathlib import Path

import pytest

import cpl

from pymetis.inputs import PipelineInputSet
Expand Down
12 changes: 0 additions & 12 deletions metisp/pymetis/src/pymetis/tests/test_metis_ifu_reduce.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,6 @@ class TestRecipe(BaseRecipeTest):
""" A bunch of extremely simple and stupid test cases... just to see if it does something """
_recipe = Recipe

@pytest.mark.skip
def test_recipe_can_be_run_directly(self, load_frameset, sof):
pass

@pytest.mark.skip
def test_can_be_run_with_pyesorex(self, name, create_pyesorex):
pass

@staticmethod
@pytest.mark.skip
def test_pyesorex_runs_with_zero_exit_code_and_empty_stderr(name, sof, create_pyesorex):
pass


class TestInputSet(BaseInputSetTest):
Expand Down

0 comments on commit adf9105

Please sign in to comment.