data_models.base

KPF-specific base data model.

Thin layer on top of RVDataModel that adds KPF-specific attributes and the behaviour shared by every KPF data model: fits.Header storage, the DRPSTATU receipt stamp, alias-aware set_data/set_header, and lossless PRIMARY serialization. All four KPF models inherit it — L0/L1 directly, and L2/L4 via multiple inheritance alongside rvdata’s RV2/RV4 (KPFDataModel listed first so its overrides win while RV2/RV4 remain reachable through super()).

class kpfpipe.data_models.base.KPFDataModel

Bases: RVDataModel

Shared base for every KPF data model (L0, L1, and — multiply-inherited with RV2/RV4 — L2, L4).

static as_fits_header(src)

Return src as an astropy.io.fits.Header, preserving comments.

KPF stores every extension header as a fits.Header so reads and writes go through astropy natively, with no value-vs-(value, comment) ambiguity. This is the single bridge from the two legacy in-memory forms:

  • a fits.Header (e.g. from from_fits) is returned as a copy, so a caller can rebuild an HDU without aliasing the stored header;

  • a plain mapping (RVData seeds PRIMARY defaults as an OrderedDict whose entries are (value, comment) tuples) is rebuilt card by card — head[kw] = (value, comment) sets the value and comment together.

check_filename_convention(filename)

Abstract: every concrete KPF model declares its own filename convention.

KPFDataModel is never instantiated directly — only inherited — so reaching this means a subclass failed to define the method.

create_extension(ext_name, ext_type, header=None, data=None)

Create an extension, storing its header as a fits.Header.

rvdata initializes a new header as a plain OrderedDict; KPF keeps every header as a fits.Header so all reads/writes are native astropy.

classmethod from_fits(fn, instrument=None, **kwargs)

Read a data product from FITS, logging the file read (DRP-RUN-08).

The single read chokepoint for every KPF data model: one INFO record per FITS read, naming the concrete class and the path, then delegate to the inherited reader (rvdata’s for L2/L4 via RV2/RV4 in the MRO).

Ensures obs_id is carried in memory after the read. L0 (and any product whose filename embeds the obs_id) resolves it during read; L1/L2/L4 filenames are timestamp-based, so a read there recovers the obs_id from the ORIGID provenance card instead (see _obs_id_from_receipt). The to_kpfN converters set obs_id directly, so this only fills the from_fits path.

generate_standard_filename()

Abstract: every concrete KPF model builds its own standard filename.

KPFDataModel is never instantiated directly — only inherited — so reaching this means a subclass failed to define the method.

keyword_registry = <kpfpipe.data_models.keyword_registry.KeywordRegistry object>
receipt_add_entry(function, args, status)

Record a processing step, and update DRPSTATU for pipeline modules.

Signature matches rvdata >=0.4.0: function names the step, args is a key=value provenance string ("" when not applicable), status is "PASS"/"FAIL".

set_data(ext_name, data)

Set extension data, resolving KPF aliases first.

For aliased models (KPF2/KPF4) this resolves chip-prefix keys (e.g. ‘GREEN_SCI2_FLUX’, routed through the data dict’s __setitem__) and extension aliases before the base class .keys() check. The hasattr guards make it a no-op passthrough for non-aliased L0/L1.

set_header(ext_name, header)

Set an extension header, resolving KPF aliases before the base class .keys() check (a no-op for non-aliased L0/L1).

set_keyword(key, value, ext=None)

Write a registered keyword to its home extension header.

Looks key up in the merged KPF + EPRV keyword registry (config/L{0,1,2,4}-headers.csv plus the EPRV PRIMARY keywords) and writes value to the extension named there, with the registry Description as the FITS comment. This is the single write path for registered keywords, so a keyword always lands on the same extension with the same comment — callers never name a comment.

ext targets a specific extension for EPRV per-extension cards that have no single routed home because they recur on every orderlet’s extension (e.g. VELSTART on CCF1..5, RVMETHOD on RV1..5). The keyword must be registered for that extension; the comment still comes from the registry. Aliases (e.g. SCI2_CCF) resolve first. When ext is None the keyword routes to its registered home as usual.

Raises:
  • KeyError – If key is registered nowhere (default), or not registered for ext (targeted); register it in the appropriate config/L{level}-headers.csv before writing it.

  • ValueError – If the target extension does not exist on this object (a config error — the extension must be created before the write).