From 1356deb427f36e2d26f44b77bc251de42d700de0 Mon Sep 17 00:00:00 2001 From: ahmedk <karim.ahmed@xfel.eu> Date: Thu, 14 Jul 2022 00:25:31 +0200 Subject: [PATCH] Retrieve constants from YAML epix/pnccd --- .../ePix100/Correction_ePix100_NBC.ipynb | 172 +++++++++++--- notebooks/pnCCD/Correct_pnCCD_NBC.ipynb | 212 ++++++++++-------- src/cal_tools/pnccdlib.py | 6 +- src/cal_tools/tools.py | 3 + 4 files changed, 256 insertions(+), 137 deletions(-) diff --git a/notebooks/ePix100/Correction_ePix100_NBC.ipynb b/notebooks/ePix100/Correction_ePix100_NBC.ipynb index 396a2ed69..f0cd9cc58 100644 --- a/notebooks/ePix100/Correction_ePix100_NBC.ipynb +++ b/notebooks/ePix100/Correction_ePix100_NBC.ipynb @@ -19,6 +19,7 @@ "source": [ "in_folder = \"/gpfs/exfel/exp/CALLAB/202031/p900113/raw\" # input folder, required\n", "out_folder = \"\" # output folder, required\n", + "metadata_folder = \"\" # Directory containing calibration_metadata.yml when run by xfel-calibrate\n", "sequences = [-1] # sequences to correct, set to -1 for all, range allowed\n", "sequences_per_node = 1 # number of sequence files per cluster node if run as slurm job, set to 0 to not run SLURM parallel\n", "run = 9988 # which run to read data from, required\n", @@ -77,25 +78,23 @@ "outputs": [], "source": [ "import tabulate\n", - "import traceback\n", "import warnings\n", "\n", "import h5py\n", "import pasha as psh\n", - "import multiprocessing\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", - "from IPython.display import Latex, Markdown, display\n", + "from IPython.display import Latex, display\n", "from extra_data import RunDirectory, H5File\n", "from pathlib import Path\n", "\n", "from XFELDetAna import xfelpyanatools as xana\n", "from XFELDetAna import xfelpycaltools as xcal\n", - "from XFELDetAna.plotting.util import prettyPlotting\n", "from cal_tools import h5_copy_except\n", "from cal_tools.tools import (\n", - " get_constant_from_db,\n", " get_dir_creation_date,\n", + " get_from_db,\n", + " CalibrationMetadata,\n", ")\n", "from cal_tools.step_timing import StepTimer\n", "from iCalibrationDB import (\n", @@ -121,7 +120,7 @@ "if absolute_gain:\n", " relative_gain = True\n", "\n", - "plot_unit = 'ADU'\n" + "plot_unit = 'ADU'" ] }, { @@ -136,6 +135,39 @@ "in_folder = Path(in_folder)\n", "ped_dir = in_folder / f\"r{run:04d}\"\n", "run_dc = RunDirectory(ped_dir, _use_voview=False)\n", + "metadata = CalibrationMetadata(metadata_folder or out_folder)\n", + "# NOTE: this notebook will not overwrite calibration metadata file\n", + "const_yaml = metadata.get(\"retrieved-constants\", {})\n", + "\n", + "fp_name = path_template.format(run, karabo_da)\n", + "\n", + "print(f\"Reading from: {ped_dir / fp_name}\")\n", + "print(f\"Run is: {run}\")\n", + "print(f\"Instrument H5File source: {instrument_src}\")\n", + "print(f\"Data corrected files are stored at: {out_folder}\")\n", + "\n", + "creation_time = None\n", + "if use_dir_creation_date:\n", + " creation_time = get_dir_creation_date(in_folder, run)\n", + "if creation_time:\n", + " print(f\"Using {creation_time.isoformat()} as creation time\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = 708 # rows of the ePix100\n", + "y = 768 # columns of the ePix100\n", + "\n", + "in_folder = Path(in_folder)\n", + "ped_dir = in_folder / f\"r{run:04d}\"\n", + "run_dc = RunDirectory(ped_dir, _use_voview=False)\n", + "metadata = CalibrationMetadata(metadata_folder or out_folder)\n", + "# NOTE: this notebook will not overwrite calibration metadata file\n", + "const_yaml = metadata.get(\"retrieved-constants\", {})\n", "\n", "fp_name = path_template.format(run, karabo_da)\n", "\n", @@ -264,6 +296,10 @@ "}\n", "\n", "dark_condition = Conditions.Dark.ePix100(**cond_dict)\n", + "for parm in dark_condition.parameters:\n", + " if parm.name == \"Sensor Temperature\":\n", + " parm.lower_deviation = temp_deviations\n", + " parm.upper_deviation = temp_deviations\n", "\n", "# update conditions with illuminated conditins.\n", "cond_dict.update({\n", @@ -276,38 +312,96 @@ " \"Offset\": dark_condition,\n", " \"Noise\": dark_condition,\n", " \"RelativeGain\": illum_condition,\n", - "}\n", - "\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ "const_data = dict()\n", + "when = dict()\n", + "# Constant paths are saved under retrieved-constants in calibration_metadata.yml\n", + "retrieved_constants = metadata.setdefault(\"retrieved-constants\", {})\n", + "\n", + "if const_yaml: # Used while reproducing corrected data.\n", + " print(f\"Using stored constants in {metadata.yamlfile}\")\n", + " for cname, mdata in const_yaml[karabo_da][\"constants\"].items():\n", + " const_data[cname] = dict()\n", + " when[cname] = mdata[\"creation-time\"]\n", + " if when[cname]:\n", + " with h5py.File(mdata[\"file-path\"], \"r\") as cf:\n", + " const_data[cname] = np.copy(\n", + " cf[f\"{mdata['dataset-name']}/data\"])\n", + " else:\n", + " const_data[cname] = None\n", + "else: # First correction attempt.\n", + " mdata_dict = dict()\n", + " mdata_dict[\"constants\"] = dict()\n", + " for cname, condition in const_cond.items():\n", + " mdata_dict[\"constants\"][cname] = dict()\n", + " # Avoid retrieving RelativeGain, if not needed for correction.\n", + " if cname == \"RelativeGain\" and not relative_gain:\n", + " const_data[cname] = None\n", + " else:\n", + " const_data[cname], mdata = get_from_db(\n", + " karabo_id=karabo_id,\n", + " karabo_da=karabo_da,\n", + " constant=getattr(Constants.ePix100, cname)(),\n", + " condition=condition,\n", + " empty_constant=None,\n", + " cal_db_interface=cal_db_interface,\n", + " creation_time=creation_time,\n", + " verbosity=2,\n", + " timeout=cal_db_timeout,\n", + " meta_only=True,\n", + " )\n", + " mdata_const = mdata.calibration_constant_version\n", + " const_mdata = mdata_dict[\"constants\"][cname]\n", + " # check if constant was successfully retrieved.\n", + " if mdata.comm_db_success:\n", + " const_mdata[\"file-path\"] = (\n", + " f\"{mdata_const.hdf5path}\" f\"{mdata_const.filename}\"\n", + " )\n", + " const_mdata[\"dataset-name\"] = mdata_const.h5path\n", + " const_mdata[\"creation-time\"] = f\"{mdata_const.begin_at}\"\n", + " mdata_dict[\"physical-detector-unit\"] = mdata_const.device_name\n", + " else:\n", + " const_mdata[\"file-path\"] = None\n", + " const_mdata[\"creation-time\"] = None" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Store constants metadata in the corresponding calibration_metadata.yml file.\n", + "if not const_yaml:\n", + " retrieved_constants[karabo_da] = mdata_dict\n", "\n", - "for cname, condition in const_cond.items():\n", - " if cname == \"RelativeGain\" and not relative_gain:\n", - " continue\n", - " # TODO: Fix this logic.\n", - " for parm in condition.parameters:\n", - " if parm.name == \"Sensor Temperature\":\n", - " parm.lower_deviation = temp_deviations\n", - " parm.upper_deviation = temp_deviations\n", - "\n", - " const_data[cname] = get_constant_from_db(\n", - " karabo_id=karabo_id,\n", - " karabo_da=karabo_da,\n", - " constant=getattr(Constants.ePix100, cname)(),\n", - " condition=condition,\n", - " empty_constant=None,\n", - " cal_db_interface=cal_db_interface,\n", - " creation_time=creation_time,\n", - " print_once=2,\n", - " timeout=cal_db_timeout\n", - " )\n", + " timestamps = dict()\n", "\n", - "if relative_gain and const_data[\"RelativeGain\"] is None:\n", - " print(\n", - " \"WARNING: RelativeGain map is requested, but not found.\\n\"\n", - " \"No gain correction will be applied\"\n", - " )\n", - " relative_gain = False\n", - " absolute_gain = False\n" + " module_timestamps = timestamps[karabo_da] = dict()\n", + " module_constants = retrieved_constants[karabo_da]\n", + "\n", + " for cname, mdata in module_constants[\"constants\"].items():\n", + " if hasattr(mdata[\"creation-time\"], 'strftime'):\n", + " mdata[\"creation-time\"] = mdata[\"creation-time\"].strftime('%y-%m-%d %H:%M')\n", + "\n", + " for cname in [\"Offset\", \"BadPixelsDark\", \"RelativeGain\"]:\n", + " if cname in module_constants[\"constants\"]:\n", + " module_timestamps[cname] = module_constants[\"constants\"][cname][\"creation-time\"]\n", + " else:\n", + " module_timestamps[cname] = \"NA\"\n", + "\n", + " time_summary = retrieved_constants.setdefault(\"time-summary\", {})\n", + " time_summary[\"SAll\"] = timestamps\n", + " metadata.save()\n", + " print(f\"Stored retrieved constants in {metadata.yamlfile}\")" ] }, { @@ -316,6 +410,14 @@ "metadata": {}, "outputs": [], "source": [ + "if relative_gain and const_data.get(\"RelativeGain\", None) is None:\n", + " print(\n", + " \"WARNING: RelativeGain map is requested, but not found.\\n\"\n", + " \"No gain correction will be applied\"\n", + " )\n", + " relative_gain = False\n", + " absolute_gain = False\n", + "\n", "# Initializing some parameters.\n", "hscale = 1\n", "stats = True\n", diff --git a/notebooks/pnCCD/Correct_pnCCD_NBC.ipynb b/notebooks/pnCCD/Correct_pnCCD_NBC.ipynb index c1350f12e..cedfdd1c9 100644 --- a/notebooks/pnCCD/Correct_pnCCD_NBC.ipynb +++ b/notebooks/pnCCD/Correct_pnCCD_NBC.ipynb @@ -18,7 +18,8 @@ "outputs": [], "source": [ "in_folder = \"/gpfs/exfel/exp/SQS/202031/p900166/raw\" # input folder\n", - "out_folder = \"/gpfs/exfel/data/scratch/ahmedk/test/remove\" # output folder\n", + "out_folder = \"/gpfs/exfel/data/scratch/ahmedk/test/remove/pnccd_correct\" # output folder\n", + "metadata_folder = \"\" # Directory containing calibration_metadata.yml when run by xfel-calibrate\n", "run = 347 # which run to read data from\n", "sequences = [-1] # sequences to correct, set to -1 for all, range allowed\n", "sequences_per_node = 1 # number of sequences running on the same slurm node.\n", @@ -98,7 +99,6 @@ "import os\n", "import warnings\n", "from pathlib import Path\n", - "from typing import Tuple\n", "warnings.filterwarnings('ignore')\n", "\n", "import h5py\n", @@ -115,14 +115,14 @@ "from XFELDetAna import xfelpycaltools as xcal\n", "from cal_tools import pnccdlib\n", "from cal_tools.tools import (\n", - " get_constant_from_db_and_time,\n", " get_dir_creation_date,\n", + " get_from_db,\n", " get_random_db_interface,\n", - " map_modules_from_folder,\n", + " CalibrationMetadata,\n", ")\n", "from cal_tools.step_timing import StepTimer\n", "from cal_tools import h5_copy_except\n", - "from iCalibrationDB import Conditions, ConstantMetaData, Constants\n", + "from iCalibrationDB import Conditions, Constants\n", "from iCalibrationDB.detectors import DetectorTypes" ] }, @@ -184,6 +184,9 @@ "source": [ "run_dc = RunDirectory(Path(in_folder) / f\"r{run:04d}\", _use_voview=False)\n", "ctrl_data = pnccdlib.PnccdCtrl(run_dc, karabo_id)\n", + "metadata = CalibrationMetadata(metadata_folder or out_folder)\n", + "# NOTE: this notebook will not overwrite calibration metadata file\n", + "const_yaml = metadata.get(\"retrieved-constants\", {})\n", "\n", "# extract control data\n", "step_timer.start()\n", @@ -314,7 +317,10 @@ "outputs": [], "source": [ "# Output Folder Creation:\n", - "os.makedirs(out_folder, exist_ok=True if overwrite else False)" + "os.makedirs(out_folder, exist_ok=True if overwrite else False)\n", + "\n", + "# Constant paths are saved under retrieved-constants in calibration_metadata.yml\n", + "retrieved_constants = metadata.setdefault(\"retrieved-constants\", {})" ] }, { @@ -330,57 +336,105 @@ "metadata": {}, "outputs": [], "source": [ - "def get_dark(db_parms: Tuple[str, int], bias_voltage: float, gain: float, integration_time: float,\n", - " fix_temperature_top: float, creation_time: str, run: str) -> dict :\n", - "# This function is to retrieve the dark constants associated with the run of interest:\n", - "\n", - " cal_db_interface, cal_db_timeout = db_parms\n", - " cal_db_interface = get_random_db_interface(cal_db_interface)\n", - " print(f'Calibration database interface: {cal_db_interface}')\n", - " \n", - " try: \n", - " Offset = None\n", - " Noise = None\n", - " BadPixelsDark = None\n", - "\n", - " constants = {} \n", - " constants['Offset'] = Offset\n", - " constants['Noise'] = Noise\n", - " constants['BadPixelsDark'] = BadPixelsDark\n", - " when = {}\n", - " \n", - " condition = Conditions.Dark.CCD(bias_voltage=bias_voltage,\n", - " integration_time=integration_time,\n", - " gain_setting=gain,\n", - " temperature=fix_temperature_top,\n", - " pixels_x=pixels_x,\n", - " pixels_y=pixels_y)\n", - " \n", - " for const in constants.keys():\n", - " constants[const], when[const] = \\\n", - " get_constant_from_db_and_time(karabo_id, karabo_da,\n", - " getattr(Constants.CCD(DetectorTypes.pnCCD), const)(),\n", - " condition,\n", - " np.zeros((pixels_x, pixels_y, 1)),\n", - " cal_db_interface,\n", - " creation_time=creation_time)\n", - " for parm in condition.parameters:\n", - " if parm.name == \"Sensor Temperature\":\n", - " print(f\"For run {run}: dark images for offset calculation \" \n", - " f\"were taken at temperature: {parm.value:0.2f} \"\n", - " f\"K @ {when['Offset']}\")\n", - " except Exception as e:\n", - " print(f\"Error: {e}\")\n", - "\n", + "display(Markdown('### Constants retrieval'))\n", + "step_timer.start()\n", "\n", - " return constants" + "conditions_dict = {\n", + " \"bias_voltage\": bias_voltage,\n", + " \"integration_time\": integration_time,\n", + " \"gain_setting\": gain,\n", + " \"temperature\": fix_temperature_top,\n", + " \"pixels_x\": pixels_x,\n", + " \"pixels_y\": pixels_y,\n", + " }\n", + "# Dark condition\n", + "dark_condition = Conditions.Dark.CCD(**conditions_dict)\n", + "# Add photon energy.\n", + "conditions_dict.update({\"photon_energy\": photon_energy})\n", + "illum_condition = Conditions.Illuminated.CCD(**conditions_dict)\n", + "\n", + "constants = dict()\n", + "when = dict()\n", + "init_constants = { # A dictionary for initializing constants. {cname: (empty_constants, condition)} # noqa\n", + " \"Offset\": (np.zeros((pixels_x, pixels_y, 1), dtype=np.float32), dark_condition), # noqa\n", + " \"Noise\": (np.zeros((pixels_x, pixels_y, 1), dtype=np.float32), dark_condition), # noqa\n", + " \"BadPixelsDark\": (np.zeros((pixels_x, pixels_y, 1), dtype=np.uint32), dark_condition), # noqa\n", + " \"RelativeGain\": (np.zeros((pixels_x, pixels_y), dtype=np.float32), illum_condition), # noqa\n", + "}\n", + "if const_yaml: # Used while reproducing corrected data.\n", + " print(f\"Using stored constants in {metadata.filename}\")\n", + " for cname, mdata in const_yaml[karabo_da][\"constants\"].items():\n", + " constants[cname] = dict()\n", + " when[cname] = mdata[\"creation-time\"]\n", + " if when[cname]:\n", + " with h5py.File(mdata[\"file-path\"], \"r\") as cf:\n", + " constants[cname] = np.copy(\n", + " cf[f\"{mdata['dataset-name']}/data\"])\n", + " else:\n", + " constants[cname] = init_constants[cname][0]\n", + "else:\n", + " mdata_dict = dict()\n", + " mdata_dict[\"constants\"] = dict()\n", + " for cname, cval in init_constants.items():\n", + " mdata_dict[\"constants\"][cname] = dict()\n", + " # No need for retrieving RelativeGain, if not used for correction.\n", + " if not corr_bools.get(\"relgain\") and cname == \"RelativeGain\":\n", + " continue\n", + " constants[cname], mdata = \\\n", + " get_from_db(\n", + " karabo_id=karabo_id,\n", + " karabo_da=karabo_da,\n", + " constant=getattr(Constants.CCD(DetectorTypes.pnCCD), cname)(),\n", + " condition=cval[1],\n", + " empty_constant=cval[0],\n", + " cal_db_interface=get_random_db_interface(cal_db_interface),\n", + " creation_time=creation_time,\n", + " verbosity=1,\n", + " )\n", + " mdata_const = mdata.calibration_constant_version\n", + " const_mdata = mdata_dict[\"constants\"][cname]\n", + " # check if constant was successfully retrieved.\n", + " if mdata.comm_db_success:\n", + " const_mdata[\"file-path\"] = (\n", + " f\"{mdata_const.hdf5path}\" f\"{mdata_const.filename}\"\n", + " )\n", + " const_mdata[\"dataset-name\"] = mdata_const.h5path\n", + " const_mdata[\"creation-time\"] = f\"{mdata_const.begin_at}\"\n", + " mdata_dict[\"physical-detector-unit\"] = mdata_const.device_name\n", + " else:\n", + " const_mdata[\"file-path\"] = None\n", + " const_mdata[\"creation-time\"] = None\n" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ - "## Retrieving calibration constants" + "# Store constants metadata in the corresponding calibration_metadata.yml file.\n", + "if not const_yaml:\n", + " retrieved_constants[karabo_da] = mdata_dict\n", + "\n", + " timestamps = dict()\n", + "\n", + " module_timestamps = timestamps[karabo_da] = dict()\n", + " module_constants = retrieved_constants[karabo_da]\n", + "\n", + " for cname, mdata in module_constants[\"constants\"].items():\n", + " if hasattr(mdata[\"creation-time\"], 'strftime'):\n", + " mdata[\"creation-time\"] = mdata[\"creation-time\"].strftime('%y-%m-%d %H:%M')\n", + "\n", + " for cname in [\"Offset\", \"BadPixelsDark\", \"RelativeGain\"]:\n", + " if cname in module_constants[\"constants\"]:\n", + " module_timestamps[cname] = module_constants[\"constants\"][cname][\"creation-time\"]\n", + " else:\n", + " module_timestamps[cname] = \"NA\"\n", + "\n", + " time_summary = retrieved_constants.setdefault(\"time-summary\", {})\n", + " time_summary[\"SAll\"] = timestamps\n", + " metadata.save()\n", + " print(f\"Stored retrieved constants in {metadata.filename}\")" ] }, { @@ -389,13 +443,6 @@ "metadata": {}, "outputs": [], "source": [ - "display(Markdown('### Dark constants retrieval'))\n", - "step_timer.start()\n", - "db_parms = cal_db_interface, cal_db_timeout\n", - "\n", - "constants = get_dark(db_parms, bias_voltage, gain, integration_time,\n", - " fix_temperature_top, creation_time, run)\n", - "\n", "fig = xana.heatmapPlot(constants[\"Offset\"][:,:,0], x_label='Columns', y_label='Rows', lut_label='Offset (ADU)', \n", " aspect=1, \n", " x_range=(0, pixels_y), y_range=(0, pixels_x), vmax=16000, \n", @@ -413,49 +460,16 @@ " aspect=1, x_range=(0, pixels_y), y_range=(0, pixels_x), \n", " panel_x_label='Row Stat (ADU)', panel_y_label='Column Stat (ADU)', \n", " title = 'Dark Bad Pixels Map')\n", - "step_timer.done_step(\"Dark constants retrieval\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ + "\n", "if corr_bools.get('relgain'):\n", - " step_timer.start()\n", - " display(Markdown('### Relative gain constant retrieval'))\n", - " metadata = ConstantMetaData()\n", - " relgain = Constants.CCD(DetectorTypes.pnCCD).RelativeGain()\n", - " metadata.calibration_constant = relgain\n", - " # set the operating condition\n", - " condition = Conditions.Illuminated.CCD(bias_voltage=bias_voltage,\n", - " integration_time=integration_time,\n", - " gain_setting=gain,\n", - " temperature=fix_temperature_top,\n", - " pixels_x=pixels_x,\n", - " pixels_y=pixels_y, \n", - " photon_energy=photon_energy)\n", - "\n", - " constants[\"RelativeGain\"], relgain_time = \\\n", - " get_constant_from_db_and_time(karabo_id, karabo_da,\n", - " Constants.CCD(DetectorTypes.pnCCD).RelativeGain(),\n", - " condition,\n", - " np.zeros((pixels_x, pixels_y)),\n", - " cal_db_interface,\n", - " creation_time=creation_time)\n", - "\n", - " print(f\"Retrieved RelativeGain constant with creation time: {relgain_time}\")\n", - "\n", - " display(Markdown('### Relative Gain Map Retrieval'))\n", " fig = xana.heatmapPlot(constants[\"RelativeGain\"], figsize=(8, 8), x_label='Columns', y_label='Rows', \n", - " lut_label='Relative Gain', \n", - " aspect=1, x_range=(0, pixels_y), y_range=(0, pixels_x), vmin=0.8, vmax=1.2, \n", - " panel_x_label='Row Stat (ADU)', panel_y_label='Column Stat (ADU)', \n", - " panel_top_low_lim = 0.5, panel_top_high_lim = 1.5, panel_side_low_lim = 0.5, \n", - " panel_side_high_lim = 1.5, \n", - " title = f'Relative Gain Map for pnCCD (Gain = 1/{int(gain)})')\n", - " step_timer.done_step(\"Relative gain constant retrieval\")" + " lut_label='Relative Gain', \n", + " aspect=1, x_range=(0, pixels_y), y_range=(0, pixels_x), vmin=0.8, vmax=1.2, \n", + " panel_x_label='Row Stat (ADU)', panel_y_label='Column Stat (ADU)', \n", + " panel_top_low_lim = 0.5, panel_top_high_lim = 1.5, panel_side_low_lim = 0.5, \n", + " panel_side_high_lim = 1.5, \n", + " title = f'Relative Gain Map for pnCCD (Gain = 1/{int(gain)})')\n", + "step_timer.done_step(\"Constants retrieval\")" ] }, { diff --git a/src/cal_tools/pnccdlib.py b/src/cal_tools/pnccdlib.py index 98594427e..4a9c298ca 100644 --- a/src/cal_tools/pnccdlib.py +++ b/src/cal_tools/pnccdlib.py @@ -24,9 +24,9 @@ class PnccdCtrl(): self.mdl_src_temp = f"{karabo_id}/MDL/{{}}" def get_bias_voltage(self): - return( - abs(self.run_dc.get_run_value( - self.mdl_src_temp.format("DAQ_MPOD"), "u0voltage.value"))) + # Convert the high float precision is not needed. + return(int(abs(self.run_dc.get_run_value( + self.mdl_src_temp.format("DAQ_MPOD"), "u0voltage.value")))) def get_gain(self): return( diff --git a/src/cal_tools/tools.py b/src/cal_tools/tools.py index db913f26f..c489f48b3 100644 --- a/src/cal_tools/tools.py +++ b/src/cal_tools/tools.py @@ -872,6 +872,9 @@ class CalibrationMetadata(dict): else: print(f"Warning: existing {self._yaml_fn} is malformed, " "will be overwritten") + @property + def filename(self): + return self._yaml_fn def save(self): with self._yaml_fn.open("w") as fd: -- GitLab