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

metis_cal_chophome skeleton #78

Merged
merged 7 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 5 additions & 1 deletion metisp/pymetis/src/pymetis/inputs/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

- `SinglePipelineInput` (for `Input` classes with a single Frame)
- `MultiplePipelineInput` (for `Input` classes with a FrameSet)

You should override class attributes:

- `_title`
Expand Down Expand Up @@ -101,3 +101,7 @@ class WavecalInput(SinglePipelineInput):
_group: cpl.ui.Frame.FrameGroup = cpl.ui.Frame.FrameGroup.CALIB


class PinholeTableInput(SinglePipelineInput):
_title: str = "pinhole table"
_tags: Pattern = re.compile(r"WCU_PINHOLE_TABLE")
_group: cpl.ui.Frame.FrameGroup = cpl.ui.Frame.FrameGroup.CALIB
184 changes: 184 additions & 0 deletions metisp/pymetis/src/pymetis/recipes/cal/metis_cal_chophome.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
"""
This file is part of the METIS Pipeline.
Copyright (C) 2024 European Southern Observatory

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
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
from typing import Dict

import cpl
from cpl.core import Msg

from pymetis.base.recipe import MetisRecipe
from pymetis.inputs import RawInput
from pymetis.inputs.common import (LinearityInput, GainMapInput,
PersistenceMapInput, BadpixMapInput,
PinholeTableInput)
from pymetis.base.product import PipelineProduct
from pymetis.prefab.rawimage import RawImageProcessor

class MetisCalChophomeImpl(RawImageProcessor): # TODO replace parent class?
"""Implementation class for metis_cal_chophome"""
target = "LM_CHOPHOME"

class InputSet(RawImageProcessor.InputSet):
"""Inputs for metis_cal_chophome"""
class RawInput(RawInput):
_tags = re.compile(r"LM_CHOPHOME_RAW")

class DarkInput(RawInput):
_tags = re.compile(r"LM_WCU_OFF_RAW")

def __init__(self, frameset: cpl.ui.FrameSet):
super().__init__(frameset)
self.raw = self.RawInput(frameset)
self.background = self.DarkInput(frameset)
self.linearity = LinearityInput(frameset)
self.gain_map = GainMapInput(frameset)
self.persistence = PersistenceMapInput(frameset, required=False)
self.badpixmap = BadpixMapInput(frameset, required=False)
self.pinhole_table = PinholeTableInput(frameset, required=True)

self.inputs += [self.raw, self.background, self.linearity, self.gain_map,
self.badpixmap, self.persistence]


class ProductCombined(PipelineProduct):
"""
Final product: combined, background-subtracted images of the WCU source
"""
group = cpl.ui.Frame.FrameGroup.PRODUCT
level = cpl.ui.Frame.FrameLevel.FINAL
frame_type = cpl.ui.Frame.FrameType.IMAGE

@property
def category(self) -> str:
return "LM_CHOPHOME_COMBINED"

@property
def output_file_name(self) -> str:
return f"{self.category}.fits"

@property
def tag(self) -> str:
return rf"{self.category}"


class ProductBackground(PipelineProduct):
"""
Intermediate product: the instrumental background (WCU OFF)
"""
group = cpl.ui.Frame.FrameGroup.PRODUCT
level = cpl.ui.Frame.FrameLevel.INTERMEDIATE
frame_type = cpl.ui.Frame.FrameType.IMAGE

@property
def category(self) -> str:
return "LM_CHOPHOME_BACKGROUND"

@property
def output_file_name(self) -> str:
return f"{self.category}.fits"

@property
def tag(self) -> str:
return rf"{self.category}"


def process_images(self) -> Dict[str, PipelineProduct]:
"""do something"""

background_hdr = cpl.core.PropertyList()
self.inputset.background.frameset.dump()
bg_images = self.load_images(self.inputset.background.frameset)
background_img = self.combine_images(bg_images, "median")
# TODO: define usedframes

combined_hdr = cpl.core.PropertyList()
raw_images = self.load_images(self.inputset.raw.frameset)
raw_images.subtract_image(background_img)
combined_img = self.combine_images(raw_images, "median")

self.products = {
rf"{self.target}_COMBINED":
self.ProductCombined(self, combined_hdr, combined_img),
rf"{self.target}_BACKGROUND":
self.ProductBackground(self, background_hdr, background_img),
}
return self.products

def load_images(self, frameset: cpl.ui.FrameSet) -> cpl.core.ImageList:
"""Load an imagelist from a FrameSet

This is a temporary implementation that should be generalised to the
entire pipeline package. It uses cpl functions - these should be
replaced with hdrl functions once they become available, in order
to use uncertainties and masks.
"""
output = cpl.core.ImageList()

for idx, frame in enumerate(frameset):
Msg.info(self.__class__.__qualname__,
f"Processing input frame #{idx}: {frame.file!r}...")
output.append(cpl.core.Image.load(frame.file, extension=1))

return output


class MetisCalChophome(MetisRecipe):
"""Determine chopper home position
"""
# Recipe information
_name = "metis_cal_chophome"
_version = "0.1"
_author = "Oliver Czoske, A*"
_email = "[email protected]"
_copyright = "GPL-3.0-or-later"
_synopsis = "Determination of chopper home position"
_description = """\
Determine the chopper home position from LM-imaging of the WCU pinhole mask.

Inputs
LM_CHOPHOME_RAW: Raw LM band images [1-n]
LM_WCU_RAW_OFF: Background images with WCU black-body closed [1-n]
GAIN_MAP_2RG: Gain map for 2RG detector
LINEARITY_2RG: Linearity map for 2RG detector
BADPIX_MAP_2RG: Bad-pixel map for 2RG detector [optional]
PINHOLE_TABLE: Table with location of pinhole on mask
PERSISTENCE_MAP: Persistence map [optional]

Outputs
LM_CHOPHOME_BACKGROUND: Average of background images (WCU_OFF)
LM_CHOPHOME_COMBINED: Stacked background-subtracted images of pinhole mask
The chopper offset is in the header.

Algorithm
The position of the pinhole image on the detector is measured from the stacked
background-subtracted images. The measured position is compared to the WFS
metrology to give the chopper home position.
"""

parameters = cpl.ui.ParameterList([
cpl.ui.ParameterEnum(
name="metis_cal_chophome.stacking.method",
context="metis_cal_chophome",
description="Name of the method used to combine the input images",
default="average",
alternatives=("add", "average", "median", "sigclip"),
),
]) # no parameters defined in DRLD
implementation_class = MetisCalChophomeImpl
2 changes: 1 addition & 1 deletion metisp/pymetis/src/pymetis/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def inner(filename: str):
frameset = cpl.ui.FrameSet()
with open(root / filename) as f:
for line in f.readlines():
tokens = line.rstrip('\n').split(' ')
tokens = line.rstrip('\n').split()
path = os.path.expandvars(tokens[0])
frameset.append(cpl.ui.Frame(path, tag=tokens[1]))

Expand Down
51 changes: 51 additions & 0 deletions metisp/pymetis/src/pymetis/tests/test_metis_cal_chophome.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""
This file is part of the METIS Pipeline.
Copyright (C) 2024 European Southern Observatory

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""

import pytest

from pymetis.recipes.cal.metis_cal_chophome import (MetisCalChophome as Recipe,
MetisCalChophomeImpl as Impl)
from generic import BaseInputSetTest, BaseRecipeTest, BaseProductTest


@pytest.fixture
def name():
return 'metis_cal_chophome'


@pytest.fixture
def sof(name):
return f"{name}.sof"


class TestRecipe(BaseRecipeTest):
""" A bunch of extremely simple test cases... just to see if it does something """
_recipe = Recipe


class TestInputSet(BaseInputSetTest):
impl = Impl
count = 3


class TestProductCombined(BaseProductTest):
product = Impl.ProductCombined

class TestProductBackground(BaseProductTest):
product = Impl.ProductBackground
3 changes: 2 additions & 1 deletion metisp/pyrecipes/metis_recipes.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from pymetis.recipes.ifu.metis_ifu_postprocess import MetisIfuPostprocess
from pymetis.recipes.ifu.metis_ifu_reduce import MetisIfuReduce
from pymetis.recipes.ifu.metis_ifu_telluric import MetisIfuTelluric
from pymetis.recipes.cal.metis_cal_chophome import MetisCalChophome

__all__ = [
MetisDetLinGain,
Expand All @@ -39,5 +40,5 @@
MetisIfuPostprocess,
MetisIfuReduce,
MetisIfuTelluric,
MetisCalChophome,
]