From 586431c385bee23eec673cb7320e00453a7e513d Mon Sep 17 00:00:00 2001 From: William Colocho Date: Fri, 31 Jul 2026 10:31:42 -0700 Subject: [PATCH 1/6] KLYS support --- slac_db/create/combined.py | 48 ++++++++++++++++++-- slac_db/create/lcls_elements.py | 33 +++++++++++++- slac_db/metadata.py | 58 ++++++++++++++++++++++++ slac_db/package_data/accessor_names.yaml | 14 +++++- 4 files changed, 148 insertions(+), 5 deletions(-) diff --git a/slac_db/create/combined.py b/slac_db/create/combined.py index 691f73c..bbdbc2c 100644 --- a/slac_db/create/combined.py +++ b/slac_db/create/combined.py @@ -3,7 +3,7 @@ import slac_db.io import slac_db.oracle import slac_db.device -from slac_db.metadata import get_wire_metadata +from slac_db.metadata import get_wire_metadata, get_klystron_metadata from pykern.pkcollections import PKDict import yaml @@ -79,10 +79,14 @@ def _build(): for r in slac_db.oracle.get_all_rows(): if r["element"] not in self.device_names: continue + cs_name = r["control system name"] or "" + # Klystron stations share keyword=LCAV with TCAVs but need + # their own accessor block keyed as "KLYS". + d_type = "KLYS" if "KLYS" in cs_name else r["keyword"] yield from _meta( r["element"], - r["control system name"], - r["keyword"], + cs_name, + d_type, ) def _get_accessors(d_type, address): @@ -114,6 +118,44 @@ def _meta(device, pv_head, d_type): self.accessor_map = slac_db.io.read_dict(_ACCESSOR_YAML) self.accessor_meta = list(_build()) + self._apply_klystron_accessor_overrides() + + def _apply_klystron_accessor_overrides(self): + """Apply per-station PV overrides from klystron_metadata.yaml. + + For special stations (K24_1, K20_6, etc.) some accessors must point to + PVs that differ from the normal KLYS:{cs_name}:{SUFFIX} pattern. + This removes any existing accessor entries for the affected + (device_name, accessor_name) pairs and inserts the override values. + Empty-string PV values mean the accessor is absent and are skipped. + """ + overrides = get_klystron_metadata() + if not overrides: + return + + # Build a set of (device_name, accessor_name) to remove, then a list + # of new PKDict entries to append. + to_remove = set() + to_add = [] + for station, fields in overrides.items(): + if station not in self.device_names: + continue + for accessor_name, cs_address in fields.items(): + if accessor_name in ("description", "in_use"): + continue + to_remove.add((station, accessor_name)) + if cs_address: # skip empty — means PV is absent + to_add.append(PKDict( + device_name=station, + accessor_name=accessor_name, + cs_address=cs_address, + )) + + self.accessor_meta = [ + e for e in self.accessor_meta + if (e.device_name, e.accessor_name) not in to_remove + ] + self.accessor_meta.extend(to_add) def _address_map(self): """Create an address map where keys are PV heads diff --git a/slac_db/create/lcls_elements.py b/slac_db/create/lcls_elements.py index 26ad239..34db9fc 100644 --- a/slac_db/create/lcls_elements.py +++ b/slac_db/create/lcls_elements.py @@ -46,7 +46,38 @@ def __init__(self, csv_source=None): def _parse_csv(self, reader): names = [r.lower() for r in next(reader)] i = 0 + # Track station names already recorded for KLYS sub-cavity dedup. + # Maps station_name -> index in self.rows for the canonical row. + # Keyed on the stripped element name (e.g. "K21_5") so that dirty + # cs_name data (truncated or transposed digits in sectors 12-19) + # cannot produce duplicate station entries. + _klys_seen = {} for row in reader: values = [None if v == '' else v for v in row] - self.rows[i] = dict(zip(names, values)) + d = dict(zip(names, values)) + element = d.get("element") or "" + cs_name = d.get("control system name") or "" + keyword = d.get("keyword") or "" + # Deduplicate klystron sub-cavities (K21_5A/B/C/D -> K21_5). + # A sub-cavity row is an LCAV whose element ends in A-D and whose + # cs_name contains "KLYS" (handles both "KLYS:LI{s}:{n}1" and + # legacy reversed form "LI{s}:KLYS:{n}1" used in sectors 12-19). + # Dedup key is the station name (element minus trailing letter) so + # that cs_name inconsistencies in the source data don't create + # duplicate entries (e.g. K12_3A has cs LI12:KLYS:3 while + # K12_3B/C/D have LI12:KLYS:31). + if ( + keyword == "LCAV" + and "KLYS" in cs_name + and len(element) > 1 + and element[-1] in "ABCD" + ): + station = element[:-1] # e.g. "K21_5A" -> "K21_5" + if station in _klys_seen: + # Already have a row for this station; skip this sub-cavity. + continue + # First time seeing this station: record it and rename element. + d["element"] = station + _klys_seen[station] = i + self.rows[i] = d i += 1 diff --git a/slac_db/metadata.py b/slac_db/metadata.py index 9ef924d..09d3be2 100644 --- a/slac_db/metadata.py +++ b/slac_db/metadata.py @@ -156,6 +156,64 @@ def get_tcav_metadata(tcav_names: List[str] = [], method: callable = None, **kwa return {} +def get_klystron_metadata() -> Dict[str, Dict[str, str]]: + """Load klystron_metadata.yaml and return per-station accessor overrides. + + Returns a dict keyed by station name (e.g. 'K24_1') whose values are flat + dicts mapping accessor_name -> cs_address for every PV override defined in + klystron_metadata.yaml. + + Beamcode-specific sub-dicts (keys 'beamcode1', 'beamcode2') are expanded + with a suffix, e.g. phase_act_pvname under beamcode1 becomes accessor + 'phase_actual_beamcode1'. + + The _pvname suffix is stripped and the remainder is used as the accessor + name, with underscores replacing the trailing '_pvname': + ampl_act_pvname -> energy_gain (maps to lcls-live ampl_act) + ampl_des_pvname -> energy_gain_des + phase_act_pvname -> phase_actual + phase_des_pvname -> phase_desired + Top-level empty-string values (e.g. accelerate_pvname: '') are stored as + accessor_name -> '' so callers know the PV is absent. + """ + _PV_FIELD_TO_ACCESSOR = { + "ampl_act_pvname": "energy_gain", + "ampl_des_pvname": "energy_gain_des", + "phase_act_pvname": "phase_actual", + "phase_des_pvname": "phase_desired", + "accelerate_pvname": "accelerate", + "swrd_pvname": "swrd", + "stat_pvname": "stat", + "hdsc_pvname": "hdsc", + "dsta_pvname": "dsta", + } + + here = slac_db.config.package_data() + yaml_path = os.path.join(here, "klystron_metadata.yaml") + with open(yaml_path) as f: + raw = yaml.safe_load(f) + + result = {} + for station, fields in raw.items(): + overrides = {} + for key, val in fields.items(): + if key == "description": + continue + if key in ("beamcode1", "beamcode2"): + bc = key # e.g. "beamcode1" + for pv_field, pv_val in val.items(): + accessor = _PV_FIELD_TO_ACCESSOR.get(pv_field) + if accessor is not None: + overrides[f"{accessor}_{bc}"] = pv_val + elif key in _PV_FIELD_TO_ACCESSOR: + overrides[_PV_FIELD_TO_ACCESSOR[key]] = val or "" + else: + # passthrough (e.g. in_use) + overrides[key] = val + result[station] = overrides + return result + + def get_pmt_metadata(pmt_names: List[str] = []): # return a data structure of the form: # { diff --git a/slac_db/package_data/accessor_names.yaml b/slac_db/package_data/accessor_names.yaml index 189ca3e..59bf4e4 100644 --- a/slac_db/package_data/accessor_names.yaml +++ b/slac_db/package_data/accessor_names.yaml @@ -89,4 +89,16 @@ LCAV: AMPL_W0CH0: amplitude_wocho INST: - QDCRAW: qdcraw \ No newline at end of file + QDCRAW: qdcraw + +KLYS: + ENLD: energy_gain + PHAS: phase + PACT: phase_actual + PDES: phase_desired + BEAMCODE1_STAT: beamcode1 + BEAMCODE2_STAT: beamcode2 + SWRD: swrd + STAT: stat + HDSC: hdsc + DSTA: dsta \ No newline at end of file From 1d48925ee05fc8fa6fc73eba814c88fa7208fa2e Mon Sep 17 00:00:00 2001 From: William Colocho Date: Fri, 31 Jul 2026 12:23:05 -0700 Subject: [PATCH 2/6] adding the databases --- slac_db/package_data/device.sqlite3 | 4 ++-- slac_db/package_data/lcls_elements.sqlite3 | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/slac_db/package_data/device.sqlite3 b/slac_db/package_data/device.sqlite3 index 8bfda6e..47c64d0 100644 --- a/slac_db/package_data/device.sqlite3 +++ b/slac_db/package_data/device.sqlite3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f41a7e4468c81061aab1ab73a5f4ad92bedf31d12fa08d1750dc97bd86456832 -size 331829248 +oid sha256:ec4c76025fd0ff2decda4ee4a0fff0bf4f889bbcf08738d9e6d89b2611cf52e8 +size 207077376 diff --git a/slac_db/package_data/lcls_elements.sqlite3 b/slac_db/package_data/lcls_elements.sqlite3 index 676b443..9cde246 100644 --- a/slac_db/package_data/lcls_elements.sqlite3 +++ b/slac_db/package_data/lcls_elements.sqlite3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f17ec5e98de1dda3b2e87ac238003440f26560d25d91f0e0633036c34db65b78 -size 765952 +oid sha256:2a065a646c6f6424e2f157408ed9c8cd590657627d174b59bd09a3dff981a009 +size 720896 From a2372ff552b59db6edd609cb12f3256b77190ddc Mon Sep 17 00:00:00 2001 From: William Colocho Date: Mon, 3 Aug 2026 15:29:32 -0700 Subject: [PATCH 3/6] stations with special PVs --- slac_db/package_data/klystron_metadata.yaml | 142 ++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 slac_db/package_data/klystron_metadata.yaml diff --git a/slac_db/package_data/klystron_metadata.yaml b/slac_db/package_data/klystron_metadata.yaml new file mode 100644 index 0000000..7a51621 --- /dev/null +++ b/slac_db/package_data/klystron_metadata.yaml @@ -0,0 +1,142 @@ +# Per-station klystron PV overrides for stations that deviate from the normal +# KLYS:LI{sector}:{station}1:{SUFFIX} pattern. +# +# Normal stations have all PVs derived algorithmically from cs_name: +# ampl_pvname = {cs_name}:ENLD +# phase_act_pvname = {cs_name}:PHAS +# phase_des_pvname = {cs_name}:PDES +# accelerate_pvname = {cs_name}:BEAMCODE{n}_STAT +# swrd_pvname = {cs_name}:SWRD +# stat_pvname = {cs_name}:STAT +# hdsc_pvname = {cs_name}:HDSC +# dsta_pvname = {cs_name}:DSTA +# +# Only fields that differ from the normal pattern are listed here. +# beamcode-dependent PVs are listed per beamcode key (1 = cu_hxr, 2 = cu_sxr). + +# --- GUN --- +K20_6: + description: GUN klystron + ampl_act_pvname: GUN:IN20:1:GN1_AAVG + ampl_des_pvname: GUN:IN20:1:GN1_ADES + phase_act_pvname: GUN:IN20:1:GN1_PAVG + phase_des_pvname: GUN:IN20:1:GN1_PDES + accelerate_pvname: '' + swrd_pvname: '' + stat_pvname: '' + hdsc_pvname: '' + dsta_pvname: '' + +# --- L0A injector linac --- +K20_7: + description: L0A injector linac + beamcode1: + ampl_act_pvname: ACCL:IN20:300:L0A_AACT_DS0 + ampl_des_pvname: ACCL:IN20:300:L0A_ADES_DS0 + phase_act_pvname: ACCL:IN20:300:L0A_PACT_DS0 + phase_des_pvname: ACCL:IN20:300:L0A_PDES_DS0 + beamcode2: + ampl_act_pvname: ACCL:IN20:300:L0A_AACT_DS1 + ampl_des_pvname: ACCL:IN20:300:L0A_ADES_DS1 + phase_act_pvname: ACCL:IN20:300:L0A_PACT_DS1 + phase_des_pvname: ACCL:IN20:300:L0A_PDES_DS1 + accelerate_pvname: '' + swrd_pvname: '' + stat_pvname: '' + hdsc_pvname: '' + dsta_pvname: '' + +# --- L0B injector linac --- +K20_8: + description: L0B injector linac + beamcode1: + ampl_act_pvname: ACCL:IN20:400:L0B_AACT_DS0 + ampl_des_pvname: ACCL:IN20:400:L0B_ADES_DS0 + phase_act_pvname: ACCL:IN20:400:L0B_PACT_DS0 + phase_des_pvname: ACCL:IN20:400:L0B_PDES_DS0 + beamcode2: + ampl_act_pvname: ACCL:IN20:400:L0B_AACT_DS1 + ampl_des_pvname: ACCL:IN20:400:L0B_ADES_DS1 + phase_act_pvname: ACCL:IN20:400:L0B_PACT_DS1 + phase_des_pvname: ACCL:IN20:400:L0B_PDES_DS1 + accelerate_pvname: '' + swrd_pvname: '' + stat_pvname: '' + hdsc_pvname: '' + dsta_pvname: '' + +# --- L1S sub-booster --- +K21_1: + description: L1S sub-booster + beamcode1: + ampl_act_pvname: ACCL:LI21:1:L1S_AACT_DS0 + ampl_des_pvname: ACCL:LI21:1:L1S_ADES_DS0 + phase_act_pvname: ACCL:LI21:1:L1S_PACT_DS0 + phase_des_pvname: ACCL:LI21:1:L1S_PDES_DS0 + beamcode2: + ampl_act_pvname: ACCL:LI21:1:L1S_AACT_DS1 + ampl_des_pvname: ACCL:LI21:1:L1S_ADES_DS1 + phase_act_pvname: ACCL:LI21:1:L1S_PACT_DS1 + phase_des_pvname: ACCL:LI21:1:L1S_PDES_DS1 + accelerate_pvname: '' + swrd_pvname: '' + stat_pvname: '' + hdsc_pvname: '' + dsta_pvname: '' + +# --- L1X 3rd-harmonic linearizer --- +K21_2: + description: L1X 3rd-harmonic linearizer + beamcode1: + ampl_act_pvname: ACCL:LI21:180:L1X_AACT_DS0 + ampl_des_pvname: ACCL:LI21:180:L1X_ADES_DS0 + phase_act_pvname: ACCL:LI21:180:L1X_PACT_DS0 + phase_des_pvname: ACCL:LI21:180:L1X_PDES_DS0 + beamcode2: + ampl_act_pvname: ACCL:LI21:180:L1X_AACT_DS1 + ampl_des_pvname: ACCL:LI21:180:L1X_ADES_DS1 + phase_act_pvname: ACCL:LI21:180:L1X_PACT_DS1 + phase_des_pvname: ACCL:LI21:180:L1X_PDES_DS1 + accelerate_pvname: '' + swrd_pvname: '' + stat_pvname: '' + hdsc_pvname: '' + dsta_pvname: '' + +# --- L2 feedback klystrons (normal ENLD/fault PVs, special phase PV) --- +K24_1: + description: L2 feedback klystron, special phase PV + beamcode1: + phase_act_pvname: ACCL:LI24:100:KLY_PDES:SETDATA_1 + phase_des_pvname: ACCL:LI24:100:KLY_PDES:SETDATA_1 + beamcode2: + phase_act_pvname: ACCL:LI24:100:KLY_PDES:SETDATA_2 + phase_des_pvname: ACCL:LI24:100:KLY_PDES:SETDATA_2 + +K24_2: + description: L2 feedback klystron, special phase PV + beamcode1: + phase_act_pvname: ACCL:LI24:200:KLY_PDES:SETDATA_1 + phase_des_pvname: ACCL:LI24:200:KLY_PDES:SETDATA_1 + beamcode2: + phase_act_pvname: ACCL:LI24:200:KLY_PDES:SETDATA_2 + phase_des_pvname: ACCL:LI24:200:KLY_PDES:SETDATA_2 + +K24_3: + description: L2 feedback klystron, special phase PV + beamcode1: + phase_act_pvname: ACCL:LI24:300:KLY_PDES:SETDATA_1 + phase_des_pvname: ACCL:LI24:300:KLY_PDES:SETDATA_1 + beamcode2: + phase_act_pvname: ACCL:LI24:300:KLY_PDES:SETDATA_2 + phase_des_pvname: ACCL:LI24:300:KLY_PDES:SETDATA_2 + +# --- Mothballed klystron (always disabled) --- +K26_3: + description: Mothballed klystron, always disabled + in_use: false + accelerate_pvname: '' + swrd_pvname: '' + stat_pvname: '' + hdsc_pvname: '' + dsta_pvname: '' From 9dac120e3023a5d92b7a56c9f50e0df2f2f85331 Mon Sep 17 00:00:00 2001 From: William Colocho Date: Wed, 5 Aug 2026 12:12:12 -0700 Subject: [PATCH 4/6] Move data from klystron_metatdata.yaml to accessr_names.yaml --- slac_db/create/combined.py | 28 ++-- slac_db/package_data/accessor_names.yaml | 81 ++++++++++- slac_db/package_data/klystron_metadata.yaml | 142 -------------------- 3 files changed, 92 insertions(+), 159 deletions(-) delete mode 100644 slac_db/package_data/klystron_metadata.yaml diff --git a/slac_db/create/combined.py b/slac_db/create/combined.py index bbdbc2c..db4c608 100644 --- a/slac_db/create/combined.py +++ b/slac_db/create/combined.py @@ -3,7 +3,7 @@ import slac_db.io import slac_db.oracle import slac_db.device -from slac_db.metadata import get_wire_metadata, get_klystron_metadata +from slac_db.metadata import get_wire_metadata from pykern.pkcollections import PKDict import yaml @@ -117,36 +117,32 @@ def _meta(device, pv_head, d_type): ] self.accessor_map = slac_db.io.read_dict(_ACCESSOR_YAML) + self.accessor_overrides = self.accessor_map.pop("_overrides", {}) self.accessor_meta = list(_build()) - self._apply_klystron_accessor_overrides() + self._apply_accessor_overrides() - def _apply_klystron_accessor_overrides(self): - """Apply per-station PV overrides from klystron_metadata.yaml. + def _apply_accessor_overrides(self): + """Apply per-device PV overrides from the _overrides block in accessor_names.yaml. - For special stations (K24_1, K20_6, etc.) some accessors must point to - PVs that differ from the normal KLYS:{cs_name}:{SUFFIX} pattern. + For special devices (injector klystrons, test stand, etc.) some accessors + must point to PVs that differ from the normal pattern-derived addresses. This removes any existing accessor entries for the affected (device_name, accessor_name) pairs and inserts the override values. Empty-string PV values mean the accessor is absent and are skipped. """ - overrides = get_klystron_metadata() - if not overrides: + if not self.accessor_overrides: return - # Build a set of (device_name, accessor_name) to remove, then a list - # of new PKDict entries to append. to_remove = set() to_add = [] - for station, fields in overrides.items(): - if station not in self.device_names: + for device_name, fields in self.accessor_overrides.items(): + if device_name not in self.device_names: continue for accessor_name, cs_address in fields.items(): - if accessor_name in ("description", "in_use"): - continue - to_remove.add((station, accessor_name)) + to_remove.add((device_name, accessor_name)) if cs_address: # skip empty — means PV is absent to_add.append(PKDict( - device_name=station, + device_name=device_name, accessor_name=accessor_name, cs_address=cs_address, )) diff --git a/slac_db/package_data/accessor_names.yaml b/slac_db/package_data/accessor_names.yaml index 53ddfa9..3e29113 100644 --- a/slac_db/package_data/accessor_names.yaml +++ b/slac_db/package_data/accessor_names.yaml @@ -95,7 +95,7 @@ INST: QDCRAW: qdcraw KLYS: - ENLD: energy_gain + ENLD: amplitude PHAS: phase PACT: phase_actual PDES: phase_desired @@ -105,3 +105,82 @@ KLYS: STAT: stat HDSC: hdsc DSTA: dsta + +# Per-device PV overrides for stations that deviate from the normal pattern. +# Keys are device MAD names; values are flat dicts of accessor_name: full_pv_address. +# An empty string means the PV is absent and the accessor is omitted from the DB. +_overrides: + # --- GUN klystron --- + K20_6: + amplitude: GUN:IN20:1:GN1_AAVG + amplitude_des: GUN:IN20:1:GN1_ADES + phase_actual: GUN:IN20:1:GN1_PAVG + phase_desired: GUN:IN20:1:GN1_PDES + + # --- L0A injector linac --- + K20_7: + amplitude_beamcode1: ACCL:IN20:300:L0A_AACT_DS0 + amplitude_des_beamcode1: ACCL:IN20:300:L0A_ADES_DS0 + phase_actual_beamcode1: ACCL:IN20:300:L0A_PACT_DS0 + phase_desired_beamcode1: ACCL:IN20:300:L0A_PDES_DS0 + amplitude_beamcode2: ACCL:IN20:300:L0A_AACT_DS1 + amplitude_des_beamcode2: ACCL:IN20:300:L0A_ADES_DS1 + phase_actual_beamcode2: ACCL:IN20:300:L0A_PACT_DS1 + phase_desired_beamcode2: ACCL:IN20:300:L0A_PDES_DS1 + + # --- L0B injector linac --- + K20_8: + amplitude_beamcode1: ACCL:IN20:400:L0B_AACT_DS0 + amplitude_des_beamcode1: ACCL:IN20:400:L0B_ADES_DS0 + phase_actual_beamcode1: ACCL:IN20:400:L0B_PACT_DS0 + phase_desired_beamcode1: ACCL:IN20:400:L0B_PDES_DS0 + amplitude_beamcode2: ACCL:IN20:400:L0B_AACT_DS1 + amplitude_des_beamcode2: ACCL:IN20:400:L0B_ADES_DS1 + phase_actual_beamcode2: ACCL:IN20:400:L0B_PACT_DS1 + phase_desired_beamcode2: ACCL:IN20:400:L0B_PDES_DS1 + + # --- L1S sub-booster --- + K21_1: + amplitude_beamcode1: ACCL:LI21:1:L1S_AACT_DS0 + amplitude_des_beamcode1: ACCL:LI21:1:L1S_ADES_DS0 + phase_actual_beamcode1: ACCL:LI21:1:L1S_PACT_DS0 + phase_desired_beamcode1: ACCL:LI21:1:L1S_PDES_DS0 + amplitude_beamcode2: ACCL:LI21:1:L1S_AACT_DS1 + amplitude_des_beamcode2: ACCL:LI21:1:L1S_ADES_DS1 + phase_actual_beamcode2: ACCL:LI21:1:L1S_PACT_DS1 + phase_desired_beamcode2: ACCL:LI21:1:L1S_PDES_DS1 + + # --- L1X 3rd-harmonic linearizer --- + K21_2: + amplitude_beamcode1: ACCL:LI21:180:L1X_AACT_DS0 + amplitude_des_beamcode1: ACCL:LI21:180:L1X_ADES_DS0 + phase_actual_beamcode1: ACCL:LI21:180:L1X_PACT_DS0 + phase_desired_beamcode1: ACCL:LI21:180:L1X_PDES_DS0 + amplitude_beamcode2: ACCL:LI21:180:L1X_AACT_DS1 + amplitude_des_beamcode2: ACCL:LI21:180:L1X_ADES_DS1 + phase_actual_beamcode2: ACCL:LI21:180:L1X_PACT_DS1 + phase_desired_beamcode2: ACCL:LI21:180:L1X_PDES_DS1 + + # --- L2 feedback klystrons (normal ENLD/fault PVs, special phase PV) --- + K24_1: + phase_actual_beamcode1: ACCL:LI24:100:KLY_PDES:SETDATA_1 + phase_desired_beamcode1: ACCL:LI24:100:KLY_PDES:SETDATA_1 + phase_actual_beamcode2: ACCL:LI24:100:KLY_PDES:SETDATA_2 + phase_desired_beamcode2: ACCL:LI24:100:KLY_PDES:SETDATA_2 + + K24_2: + phase_actual_beamcode1: ACCL:LI24:200:KLY_PDES:SETDATA_1 + phase_desired_beamcode1: ACCL:LI24:200:KLY_PDES:SETDATA_1 + phase_actual_beamcode2: ACCL:LI24:200:KLY_PDES:SETDATA_2 + phase_desired_beamcode2: ACCL:LI24:200:KLY_PDES:SETDATA_2 + + K24_3: + phase_actual_beamcode1: ACCL:LI24:300:KLY_PDES:SETDATA_1 + phase_desired_beamcode1: ACCL:LI24:300:KLY_PDES:SETDATA_1 + phase_actual_beamcode2: ACCL:LI24:300:KLY_PDES:SETDATA_2 + phase_desired_beamcode2: ACCL:LI24:300:KLY_PDES:SETDATA_2 + + # --- Test Stand Station --- + K26_3: + is_ignored: KLYS:LI26:31:IGNORE.RVAL + accelerate: KLYS:LI26:31:TMODE_DES.RVAL diff --git a/slac_db/package_data/klystron_metadata.yaml b/slac_db/package_data/klystron_metadata.yaml deleted file mode 100644 index 7a51621..0000000 --- a/slac_db/package_data/klystron_metadata.yaml +++ /dev/null @@ -1,142 +0,0 @@ -# Per-station klystron PV overrides for stations that deviate from the normal -# KLYS:LI{sector}:{station}1:{SUFFIX} pattern. -# -# Normal stations have all PVs derived algorithmically from cs_name: -# ampl_pvname = {cs_name}:ENLD -# phase_act_pvname = {cs_name}:PHAS -# phase_des_pvname = {cs_name}:PDES -# accelerate_pvname = {cs_name}:BEAMCODE{n}_STAT -# swrd_pvname = {cs_name}:SWRD -# stat_pvname = {cs_name}:STAT -# hdsc_pvname = {cs_name}:HDSC -# dsta_pvname = {cs_name}:DSTA -# -# Only fields that differ from the normal pattern are listed here. -# beamcode-dependent PVs are listed per beamcode key (1 = cu_hxr, 2 = cu_sxr). - -# --- GUN --- -K20_6: - description: GUN klystron - ampl_act_pvname: GUN:IN20:1:GN1_AAVG - ampl_des_pvname: GUN:IN20:1:GN1_ADES - phase_act_pvname: GUN:IN20:1:GN1_PAVG - phase_des_pvname: GUN:IN20:1:GN1_PDES - accelerate_pvname: '' - swrd_pvname: '' - stat_pvname: '' - hdsc_pvname: '' - dsta_pvname: '' - -# --- L0A injector linac --- -K20_7: - description: L0A injector linac - beamcode1: - ampl_act_pvname: ACCL:IN20:300:L0A_AACT_DS0 - ampl_des_pvname: ACCL:IN20:300:L0A_ADES_DS0 - phase_act_pvname: ACCL:IN20:300:L0A_PACT_DS0 - phase_des_pvname: ACCL:IN20:300:L0A_PDES_DS0 - beamcode2: - ampl_act_pvname: ACCL:IN20:300:L0A_AACT_DS1 - ampl_des_pvname: ACCL:IN20:300:L0A_ADES_DS1 - phase_act_pvname: ACCL:IN20:300:L0A_PACT_DS1 - phase_des_pvname: ACCL:IN20:300:L0A_PDES_DS1 - accelerate_pvname: '' - swrd_pvname: '' - stat_pvname: '' - hdsc_pvname: '' - dsta_pvname: '' - -# --- L0B injector linac --- -K20_8: - description: L0B injector linac - beamcode1: - ampl_act_pvname: ACCL:IN20:400:L0B_AACT_DS0 - ampl_des_pvname: ACCL:IN20:400:L0B_ADES_DS0 - phase_act_pvname: ACCL:IN20:400:L0B_PACT_DS0 - phase_des_pvname: ACCL:IN20:400:L0B_PDES_DS0 - beamcode2: - ampl_act_pvname: ACCL:IN20:400:L0B_AACT_DS1 - ampl_des_pvname: ACCL:IN20:400:L0B_ADES_DS1 - phase_act_pvname: ACCL:IN20:400:L0B_PACT_DS1 - phase_des_pvname: ACCL:IN20:400:L0B_PDES_DS1 - accelerate_pvname: '' - swrd_pvname: '' - stat_pvname: '' - hdsc_pvname: '' - dsta_pvname: '' - -# --- L1S sub-booster --- -K21_1: - description: L1S sub-booster - beamcode1: - ampl_act_pvname: ACCL:LI21:1:L1S_AACT_DS0 - ampl_des_pvname: ACCL:LI21:1:L1S_ADES_DS0 - phase_act_pvname: ACCL:LI21:1:L1S_PACT_DS0 - phase_des_pvname: ACCL:LI21:1:L1S_PDES_DS0 - beamcode2: - ampl_act_pvname: ACCL:LI21:1:L1S_AACT_DS1 - ampl_des_pvname: ACCL:LI21:1:L1S_ADES_DS1 - phase_act_pvname: ACCL:LI21:1:L1S_PACT_DS1 - phase_des_pvname: ACCL:LI21:1:L1S_PDES_DS1 - accelerate_pvname: '' - swrd_pvname: '' - stat_pvname: '' - hdsc_pvname: '' - dsta_pvname: '' - -# --- L1X 3rd-harmonic linearizer --- -K21_2: - description: L1X 3rd-harmonic linearizer - beamcode1: - ampl_act_pvname: ACCL:LI21:180:L1X_AACT_DS0 - ampl_des_pvname: ACCL:LI21:180:L1X_ADES_DS0 - phase_act_pvname: ACCL:LI21:180:L1X_PACT_DS0 - phase_des_pvname: ACCL:LI21:180:L1X_PDES_DS0 - beamcode2: - ampl_act_pvname: ACCL:LI21:180:L1X_AACT_DS1 - ampl_des_pvname: ACCL:LI21:180:L1X_ADES_DS1 - phase_act_pvname: ACCL:LI21:180:L1X_PACT_DS1 - phase_des_pvname: ACCL:LI21:180:L1X_PDES_DS1 - accelerate_pvname: '' - swrd_pvname: '' - stat_pvname: '' - hdsc_pvname: '' - dsta_pvname: '' - -# --- L2 feedback klystrons (normal ENLD/fault PVs, special phase PV) --- -K24_1: - description: L2 feedback klystron, special phase PV - beamcode1: - phase_act_pvname: ACCL:LI24:100:KLY_PDES:SETDATA_1 - phase_des_pvname: ACCL:LI24:100:KLY_PDES:SETDATA_1 - beamcode2: - phase_act_pvname: ACCL:LI24:100:KLY_PDES:SETDATA_2 - phase_des_pvname: ACCL:LI24:100:KLY_PDES:SETDATA_2 - -K24_2: - description: L2 feedback klystron, special phase PV - beamcode1: - phase_act_pvname: ACCL:LI24:200:KLY_PDES:SETDATA_1 - phase_des_pvname: ACCL:LI24:200:KLY_PDES:SETDATA_1 - beamcode2: - phase_act_pvname: ACCL:LI24:200:KLY_PDES:SETDATA_2 - phase_des_pvname: ACCL:LI24:200:KLY_PDES:SETDATA_2 - -K24_3: - description: L2 feedback klystron, special phase PV - beamcode1: - phase_act_pvname: ACCL:LI24:300:KLY_PDES:SETDATA_1 - phase_des_pvname: ACCL:LI24:300:KLY_PDES:SETDATA_1 - beamcode2: - phase_act_pvname: ACCL:LI24:300:KLY_PDES:SETDATA_2 - phase_des_pvname: ACCL:LI24:300:KLY_PDES:SETDATA_2 - -# --- Mothballed klystron (always disabled) --- -K26_3: - description: Mothballed klystron, always disabled - in_use: false - accelerate_pvname: '' - swrd_pvname: '' - stat_pvname: '' - hdsc_pvname: '' - dsta_pvname: '' From fff9f486712e1dade4d94dfa956c7e520f79eb1e Mon Sep 17 00:00:00 2001 From: William Colocho Date: Mon, 10 Aug 2026 12:59:53 -0700 Subject: [PATCH 5/6] one pass loop for _overrides and db dictionaries --- slac_db/create/combined.py | 42 ++++++++------------------------------ 1 file changed, 9 insertions(+), 33 deletions(-) diff --git a/slac_db/create/combined.py b/slac_db/create/combined.py index db4c608..1564edd 100644 --- a/slac_db/create/combined.py +++ b/slac_db/create/combined.py @@ -103,6 +103,7 @@ def _get_accessors(d_type, address): yield (address, accessor) def _meta(device, pv_head, d_type): + override = self.accessor_overrides.get(device, {}) for pv_tail in self.address_map.get(pv_head, [None]): if pv_tail is None: continue @@ -114,44 +115,19 @@ def _meta(device, pv_head, d_type): accessor_name=accessor, ) for address, accessor in accessor_names + if accessor not in override ] + for accessor_name, cs_address in override.items(): + if cs_address: + yield PKDict( + device_name=device, + cs_address=cs_address, + accessor_name=accessor_name, + ) self.accessor_map = slac_db.io.read_dict(_ACCESSOR_YAML) self.accessor_overrides = self.accessor_map.pop("_overrides", {}) self.accessor_meta = list(_build()) - self._apply_accessor_overrides() - - def _apply_accessor_overrides(self): - """Apply per-device PV overrides from the _overrides block in accessor_names.yaml. - - For special devices (injector klystrons, test stand, etc.) some accessors - must point to PVs that differ from the normal pattern-derived addresses. - This removes any existing accessor entries for the affected - (device_name, accessor_name) pairs and inserts the override values. - Empty-string PV values mean the accessor is absent and are skipped. - """ - if not self.accessor_overrides: - return - - to_remove = set() - to_add = [] - for device_name, fields in self.accessor_overrides.items(): - if device_name not in self.device_names: - continue - for accessor_name, cs_address in fields.items(): - to_remove.add((device_name, accessor_name)) - if cs_address: # skip empty — means PV is absent - to_add.append(PKDict( - device_name=device_name, - accessor_name=accessor_name, - cs_address=cs_address, - )) - - self.accessor_meta = [ - e for e in self.accessor_meta - if (e.device_name, e.accessor_name) not in to_remove - ] - self.accessor_meta.extend(to_add) def _address_map(self): """Create an address map where keys are PV heads From 93115f924d1e6be52a43617bb328c21686b63120 Mon Sep 17 00:00:00 2001 From: William Colocho Date: Mon, 10 Aug 2026 15:00:00 -0700 Subject: [PATCH 6/6] remove get_klystron_metadata() --- slac_db/metadata.py | 58 --------------------------------------------- 1 file changed, 58 deletions(-) diff --git a/slac_db/metadata.py b/slac_db/metadata.py index e0ff4fe..7c5292d 100644 --- a/slac_db/metadata.py +++ b/slac_db/metadata.py @@ -156,64 +156,6 @@ def get_tcav_metadata(tcav_names: List[str] = [], method: callable = None, **kwa return {} -def get_klystron_metadata() -> Dict[str, Dict[str, str]]: - """Load klystron_metadata.yaml and return per-station accessor overrides. - - Returns a dict keyed by station name (e.g. 'K24_1') whose values are flat - dicts mapping accessor_name -> cs_address for every PV override defined in - klystron_metadata.yaml. - - Beamcode-specific sub-dicts (keys 'beamcode1', 'beamcode2') are expanded - with a suffix, e.g. phase_act_pvname under beamcode1 becomes accessor - 'phase_actual_beamcode1'. - - The _pvname suffix is stripped and the remainder is used as the accessor - name, with underscores replacing the trailing '_pvname': - ampl_act_pvname -> energy_gain (maps to lcls-live ampl_act) - ampl_des_pvname -> energy_gain_des - phase_act_pvname -> phase_actual - phase_des_pvname -> phase_desired - Top-level empty-string values (e.g. accelerate_pvname: '') are stored as - accessor_name -> '' so callers know the PV is absent. - """ - _PV_FIELD_TO_ACCESSOR = { - "ampl_act_pvname": "energy_gain", - "ampl_des_pvname": "energy_gain_des", - "phase_act_pvname": "phase_actual", - "phase_des_pvname": "phase_desired", - "accelerate_pvname": "accelerate", - "swrd_pvname": "swrd", - "stat_pvname": "stat", - "hdsc_pvname": "hdsc", - "dsta_pvname": "dsta", - } - - here = slac_db.config.package_data() - yaml_path = os.path.join(here, "klystron_metadata.yaml") - with open(yaml_path) as f: - raw = yaml.safe_load(f) - - result = {} - for station, fields in raw.items(): - overrides = {} - for key, val in fields.items(): - if key == "description": - continue - if key in ("beamcode1", "beamcode2"): - bc = key # e.g. "beamcode1" - for pv_field, pv_val in val.items(): - accessor = _PV_FIELD_TO_ACCESSOR.get(pv_field) - if accessor is not None: - overrides[f"{accessor}_{bc}"] = pv_val - elif key in _PV_FIELD_TO_ACCESSOR: - overrides[_PV_FIELD_TO_ACCESSOR[key]] = val or "" - else: - # passthrough (e.g. in_use) - overrides[key] = val - result[station] = overrides - return result - - def get_pmt_metadata(pmt_names: List[str] = []): # return a data structure of the form: # {