diff --git a/docs/source/advanced.rst b/docs/source/advanced.rst index c9c707b1fd84ffd2e7136e5f464f75ee2a7caef8..f2fb149984a442afeb8dd77a92b803cde20c311e 100644 --- a/docs/source/advanced.rst +++ b/docs/source/advanced.rst @@ -108,4 +108,98 @@ This can be useful to add user requests while running. For this: manually, assuming the correction notebook allows run and overwrite paramters:: xfel-calibrate ...... --run XYZ,ZXY-YYS --overwrite + + +Using a Parameter Generator Function +------------------------------------ + +By default, the parameters to be exposed to the command line are deduced from the +first code cell of the notebook, after resolving the notebook itself from the +detector and characterization type. For some applications it might be beneficial +to define a context-specific parameter range within the same notebook, based on +additional user input. This can be done via a parameter generation function which +is defined in one of the code cell:: + + def extend_parms(detector_instance): + from iCalibrationDB import Conditions + import inspect + existing = set() + def extract_parms(cls): + args, varargs, varkw, defaults = inspect.getargspec(cls.__init__) + pList = [] + for i, arg in enumerate(args[1:][::-1]): + if arg in existing: + continue + + existing.add(arg) + + if i < len(defaults): + default = defaults[::-1][i] + if str(default).isdigit(): + pList.append("{} = {}".format(arg, default)) + elif default is None or default == "None": + pList.append("{} = \"None\"".format(arg)) + else: + pList.append("{} = \"{}\"".format(arg, default)) + else: + pList.append("{} = 0. # required".format(arg)) + return set(pList[::-1]) # mandatories first + dtype = "LPD" if "LPD" in detector_instance.upper() else "AGIPD" + all_conditions = set() + for c in dir(Conditions): + if c[:2] != "__": + condition = getattr(Conditions, c) + parms = extract_parms(getattr(condition, dtype)) + [all_conditions.add(p) for p in parms] + return "\n".join(all_conditions) + + +.. note:: + + Note how all imports are inlined, as the function is executed outside the + notebook context. + +In the example, the function generates a list of additional parameters depending +on the `detector_instance` given. Here, `detector_instance` is defined in the first +code cell the usual way. Any other parameters defined such, that have names matching +those of the generator function signature are passed to this function. The function +should then return a string containing additional code to be appended to the first +code cell. + +To make use of this functionality, the parameter generator function needs to be +configured in `notebooks.py`, e.g. :: + + ... + "GENERIC": { + "DBTOH5": { + "notebook": "notebooks/generic/DB_Constants_to_HDF5_NBC.ipynb", + "concurrency": {"parameter": None, + "default concurrency": None, + "cluster cores": 32}, + "extend parms": "extend_parms", + }, + } + ... +To generically query which parameters are defined in the first code cell, the +code execution history feature of iPython can be used:: + + ip = get_ipython() + session = ip.history_manager.get_last_session_id() + first_cell = next(ip.history_manager.get_range(session, 1, 2, raw=True)) + _, _, code = first_cell + code = code.split("\n") + parms = {} + for c in code: + n, v = c.split("=") + n = n.strip() + v = v.strip() + try: + parms[n] = float(v) + except: + parms[n] = str(v) if not isinstance(v, str) else v + if parms[n] == "None" or parms[n] == "'None'": + parms[n] = None + +This will create a dictionary `parms` which contains all parameters either +as `float` or `str` values. \ No newline at end of file diff --git a/notebooks/generic/DB_Constants_to_HDF5_NBC.ipynb b/notebooks/generic/DB_Constants_to_HDF5_NBC.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..a15dc3ff49b603f4e1a465ba26e43a78311818f1 --- /dev/null +++ b/notebooks/generic/DB_Constants_to_HDF5_NBC.ipynb @@ -0,0 +1,267 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Constants from DB to HDF5 #\n", + "\n", + "Version 0.1, Author: S. Hauf\n", + "\n", + "Currently available instances are LPD1M1 and AGIPD1M1" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "detector_instance = \"LPD1M1\" # the detector instance to get constants for e.g. LPD1M1, required\n", + "out_file = \"/gpfs/exfel/data/scratch/haufs/test/test.h5\" # HDF5 file to output constants into, required\n", + "valid_at = \"\" # ISO formatted date for which constants shoudl be valid. Leave empty to get most current ones\n", + "cal_db_interface = \"tcp://max-exfl015:5005\"\n", + "modules = [-1] # modules to get data from, in terms of numerical quadrant indices, range allowed" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "dtype = \"LPD\" if \"LPD\" in detector_instance.upper() else \"AGIPD\"\n", + "darkconst = [\"Offset\", \"Noise\", \"SlopesPC\", \"SlopesCI\", \"BadPixelsDark\", \"BadPixelsPC\", \"BadPixelsCI\"]\n", + "skip = [\"BadPixels\"]\n", + "\n", + "overwrites = {\"LPD\": {\"SlopesFF\": {\"memory_cells\": 1},\n", + " \"BadPixelsFF\": {\"memory_cells\": 1}}}\n", + "\n", + "if modules[0] == -1:\n", + " modules = list(range(16))" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import copy\n", + "import datetime\n", + "import h5py\n", + "import inspect\n", + "\n", + "from iCalibrationDB import ConstantMetaData, Constants, Conditions, Detectors, Versions\n", + "\n", + "def extend_parms(detector_instance):\n", + " from iCalibrationDB import Conditions\n", + " import inspect\n", + " existing = set()\n", + " def extract_parms(cls):\n", + " args, varargs, varkw, defaults = inspect.getargspec(cls.__init__)\n", + " pList = [] \n", + " for i, arg in enumerate(args[1:][::-1]):\n", + " if arg in existing:\n", + " continue\n", + " \n", + " existing.add(arg)\n", + " \n", + " if i < len(defaults):\n", + " default = defaults[::-1][i] \n", + " if str(default).isdigit():\n", + " pList.append(\"{} = {}\".format(arg, default))\n", + " elif default is None or default == \"None\":\n", + " pList.append(\"{} = \\\"None\\\"\".format(arg))\n", + " else:\n", + " pList.append(\"{} = \\\"{}\\\"\".format(arg, default))\n", + " else:\n", + " pList.append(\"{} = 0. # required\".format(arg))\n", + " return set(pList[::-1]) # mandatories first\n", + " dtype = \"LPD\" if \"LPD\" in detector_instance.upper() else \"AGIPD\"\n", + " all_conditions = set()\n", + " for c in dir(Conditions):\n", + " if c[:2] != \"__\":\n", + " condition = getattr(Conditions, c)\n", + " parms = extract_parms(getattr(condition, dtype))\n", + " [all_conditions.add(p) for p in parms]\n", + " return \"\\n\".join(all_conditions)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "det = getattr(Detectors, detector_instance)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "ip = get_ipython()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "first_cell = next(ip.history_manager.get_range(ip.history_manager.get_last_session_id(), 1, 2, raw=True))" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "_, _, code = first_cell\n", + "code = code.split(\"\\n\")\n", + "parms = {}\n", + "for c in code:\n", + " n, v = c.split(\"=\")\n", + " n = n.strip()\n", + " v = v.strip()\n", + " try:\n", + " parms[n] = float(v)\n", + " except:\n", + " parms[n] = str(v) if not isinstance(v, str) else v\n", + " if parms[n] == \"None\" or parms[n] == \"'None'\":\n", + " parms[n] = None" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Failed for const RelativeGain of Q2M1: Error sending to database: {'reason': '\\'NoneType\\' object has no attribute \\'get\\': File \"/gpfs/exfel/data/scratch/haufs/karabo-2.2.1/karabo/devices/calibrationDbRemote/src/calibrationDBRemote/calibration_db_remote.py\", line 373, in zmq_server_runner\\n krb_ccv)\\n File \"/gpfs/exfel/data/scratch/haufs/karabo-2.2.1/karabo/devices/calibrationDbRemote/src/calibrationDBRemote/calibration_karabo.py\", line 53, in get_calib_const_version_file\\n file_name_abs_url = \\'{0}{1}\\'.format(ccv_krb_h.get(\\'hdf5path\\'),\\n', 'success': False}\n" + ] + } + ], + "source": [ + "ofile = h5py.File(out_file, \"w\")\n", + "\n", + "detector = getattr(Detectors, detector_instance)\n", + "for i in modules:\n", + " qm = \"Q{}M{}\".format(i//4+1, i%4+1)\n", + " module = getattr(detector, qm)\n", + " dconstants = getattr(Constants, dtype)\n", + " for const in dir(dconstants):\n", + " if const[:2] != \"__\":\n", + " \n", + " if const in skip:\n", + " continue\n", + " \n", + " cparms = copy.copy(parms)\n", + " if dtype in overwrites:\n", + " do = overwrites[dtype]\n", + " if const in do:\n", + " for arg, v in do[const].items():\n", + " cparms[arg] = v\n", + " \n", + " try:\n", + " metadata = ConstantMetaData()\n", + " cons = getattr(dconstants, const)()\n", + " metadata.calibration_constant = cons\n", + "\n", + " # set the operating condition\n", + "\n", + " cond = Conditions.Dark if const in darkconst else Conditions.Illuminated\n", + "\n", + " condition = getattr(cond, dtype)\n", + "\n", + " args, varargs, varkw, defaults = inspect.getargspec(condition.__init__)\n", + " alist = []\n", + " plist = {}\n", + " for i, arg in enumerate(args[1:][::-1]):\n", + " #if i < len(defaults):\n", + " # plist[arg] = parms[arg]\n", + " #else:\n", + " # alist.append(parms[arg])\n", + " plist[arg] = cparms[arg]\n", + "\n", + "\n", + " condition = condition(**plist)\n", + "\n", + " metadata.detector_condition = condition\n", + "\n", + " # specify the a version for this constant\n", + " if valid_at is None or valid_at == \"\":\n", + " creation_time = datetime.datetime.now()\n", + " metadata.calibration_constant_version = Versions.Now(\n", + " device=module)\n", + " else:\n", + " metadata.calibration_constant_version = Versions.Timespan(\n", + " device=module,\n", + " start=valid_at)\n", + " creation_time = valid_at\n", + "\n", + " ctime = creation_time.isoformat() if not isinstance(creation_time, str) else creation_time\n", + "\n", + " metadata.retrieve(cal_db_interface, when=ctime)\n", + " \n", + " ofile[\"{}/{}/data\".format(qm, const)] = metadata.calibration_constant.data\n", + " except Exception as e:\n", + " print(\"Failed for const {} of {}: {}\".format(const, qm, e))\n", + "ofile.close() " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.4.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}