Writing PyCPL recipes with PyHDRL¶
This section shows how to turn PyHDRL high-level algorithms into a
pipeline recipe based on cpl.ui.PyRecipe. The examples are drawn
from the hdrldemo Python recipes (hdrldemo_pyfpn,
hdrldemo_pymaglim, hdrldemo_pyresample). They are intentionally
high level: the goal is the common skeleton and the PyHDRL-specific
steps, not a line-by-line copy of the demo pipeline.
For the general PyCPL recipe model (PyRecipe, parameters,
framesets, PyEsoRex), see the PyCPL User Guide. Algorithm details
are in High-level algorithms and the API Reference.
Common recipe skeleton¶
A PyHDRL recipe is still a normal PyCPL recipe. The class inherits
from cpl.ui.PyRecipe, declares metadata, builds a
cpl.ui.ParameterList in __init__, and implements run:
import cpl.core
import cpl.ui
import cpl.dfs
import hdrl.func
class MyRecipe(cpl.ui.PyRecipe):
_name = "my_recipe"
_version = "1.0.0"
_author = "..."
_email = "..."
_copyright = "GPL-2.0-or-later"
_synopsis = "Short one-line description"
_description = "Longer help text shown by PyEsoRex / EsoRex."
def __init__(self) -> None:
super().__init__()
self.parameters = cpl.ui.ParameterList()
# append cpl.ui.ParameterValue / ParameterRange / ...
def run(self, frameset: cpl.ui.FrameSet, settings: dict) -> cpl.ui.FrameSet:
# 1. apply settings, read parameters
# 2. classify SOF frames, load data
# 3. call hdrl.func / hdrl.core
# 4. save products with cpl.dfs, return product FrameSet
...
run receives the input Set-Of-Frames (SOF) as a cpl.ui.FrameSet
and a settings dictionary of parameter overrides. It must return a
cpl.ui.FrameSet of product frames.
Reading recipe parameters¶
Build parameters in __init__ with a stable full name
(recipe_name.param), a context (usually the recipe name), and a
CLI alias for PyEsoRex:
par = cpl.ui.ParameterValue(
name="my_recipe.ext-nb-raw",
context="my_recipe",
description="FITS extension of the RAW",
default=0,
)
par.cli_alias = "ext-r"
self.parameters.append(par)
At the start of run, apply settings and then read values from
self.parameters:
for key, value in settings.items():
try:
self.parameters[key].value = value
except KeyError:
cpl.core.Msg.warning(self.name, f"Unknown setting {key}")
ext_r = self.parameters["my_recipe.ext-nb-raw"].value
String parameters that map to PyHDRL enums (resampling method, Maglim border handling, collapse mode method, …) are usually converted explicitly before constructing the PyHDRL objects.
Reading SOF data¶
Typical pattern:
Assign
frame.group(RAW/CALIB/PRODUCT) from DO tags so DFS product headers are correct.Select frames by
frame.tag(for exampleRAW,RAW_BPM,POWERSPEC_MASK).Load pixels with PyCPL:
cpl.core.Image.load,cpl.core.ImageList.load,cpl.core.Mask.load, and headers withcpl.core.PropertyList.load.Propagate quality / BPM into the data image (for example
image.reject_from_mask(...)) before calling PyHDRL.
Extension indices follow CPL conventions (primary HDU is 0). Demo
recipes often expose ext-r / ext-b / ext-e parameters for
this.
Calling PyHDRL¶
Keep the computational core small: build the PyHDRL parameter objects from recipe parameters, then call one high-level entry point. Examples:
Fixed pattern noise:
hdrl.func.fpn_compute(...)(Example: fixed pattern noise (hdrldemo_pyfpn))Limiting magnitude:
hdrl.func.Maglim(...).compute(image)(Example: limiting magnitude (hdrldemo_pymaglim))Resampling:
hdrl.func.Resample.compute(table, method, outgrid, wcs)(Example: resampling (hdrldemo_pyresample))
Where an algorithm needs an error plane, wrap PyCPL images in
hdrl.core.Image / hdrl.core.ImageList first (see
Image and cube resampling).
Saving products¶
Use cpl.dfs so products get standard pipeline provenance
(ESO PRO CATG, used frames, recipe name, pipe id). Common helpers:
cpl.dfs.save_image— image product (FPN power spectrum)cpl.dfs.save_propertylist— header / QC-only product (Maglim QC)cpl.dfs.save_table— table product (optional resample table)
Then append a cpl.ui.Frame with group=PRODUCT and the product
tag to the returned frameset:
product_frames = cpl.ui.FrameSet()
cpl.dfs.save_image(
frameset, self.parameters, usedframes,
image, "my_recipe", applist, PIPE_ID, outfile,
)
product_frames.append(
cpl.ui.Frame(
file=outfile,
tag="MY_PRODUCT",
group=cpl.ui.Frame.FrameGroup.PRODUCT,
level=cpl.ui.Frame.FrameLevel.FINAL,
frameType=cpl.ui.Frame.FrameType.IMAGE,
)
)
return product_frames
applist is a cpl.core.PropertyList that at least sets
ESO PRO CATG and any QC keywords the recipe must publish.
What differs per demo recipe¶
Each of the following pages focuses on one hdrldemo recipe and the PyHDRL API it uses. Shared SOF / DFS boilerplate is only repeated where it matters for that algorithm.