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..671479d9cdef689c10e7abe18c0e27bd29beb2ed --- /dev/null +++ b/notebooks/generic/DB_Constants_to_HDF5_NBC.ipynb @@ -0,0 +1,271 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Constants from DB to HDF5 #\n", + "\n", + "Version 0.1, Author: S. Hauf" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "detector_instance = \"LPD1M1\" # the detector instance to get constants for, 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": 13, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'pixels_y = 0. # required\\ncapacitor = \"\" # required\\nmemory_cells = 0. # required\\npixels_x = 0. # required\\nphoton_energy = 0. # required\\nbias_voltage = 0. # required\\nbeam_energy = \"\" # required'" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "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", + " \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 i < len(defaults):\n", + " default = defaults[::-1][i]\n", + " if str(default).isdigit():\n", + " pList.append(\"{} = 0. # required\".format(arg))\n", + " elif default is None or default == \"None\":\n", + " pList.append(\"{} = \\\"\\\" # required\".format(arg))\n", + " else:\n", + " pList.append(\"{} = \\\"\\\" # required\".format(arg))\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 +} diff --git a/xfel_calibrate/calibrate.py b/xfel_calibrate/calibrate.py index 67e79a995a1c52f1dafb1ae5cbee95ca0df2d58d..63d5e61866251767201e16fd5e3506c5025a9042 100755 --- a/xfel_calibrate/calibrate.py +++ b/xfel_calibrate/calibrate.py @@ -1,6 +1,7 @@ #!/usr/bin/env python import argparse +import copy from datetime import datetime import nbconvert import nbformat @@ -27,21 +28,25 @@ class RawTypeFormatter(argparse.RawDescriptionHelpFormatter, # The argument parser for calibrate.py, will be extended depending # on the options given. -parser = argparse.ArgumentParser(description="Main entry point " - "for offline calibration", - formatter_class=RawTypeFormatter) -parser.add_argument('detector', metavar='DETECTOR', type=str, - help='The detector to calibrate') +def make_initial_parser(): + parser = argparse.ArgumentParser(description="Main entry point " + "for offline calibration", + formatter_class=RawTypeFormatter) -parser.add_argument('type', metavar='TYPE', type=str, - help='Type of calibration: '+",".join(notebooks.keys())) + parser.add_argument('detector', metavar='DETECTOR', type=str, + help='The detector to calibrate') -parser.add_argument('--no-cluster-job', - action="store_true", - default=False, - help="Do not run as a cluster job") + parser.add_argument('type', metavar='TYPE', type=str, + help='Type of calibration: '+",".join(notebooks.keys())) + parser.add_argument('--no-cluster-job', + action="store_true", + default=False, + help="Do not run as a cluster job") + return parser + +parser = make_initial_parser() # helper functions for parser extensions def make_intelli_list(ltype): @@ -52,7 +57,7 @@ def make_intelli_list(ltype): """ class IntelliListAction(argparse.Action): - ltype = ltype + def __init__(self, *args, **kwargs): super(IntelliListAction, self).__init__(*args, **kwargs) @@ -74,6 +79,7 @@ def make_intelli_list(ltype): parsed_values = [self.ltype(p) for p in parsed_values] print("Parsed input {} to {}".format(values, parsed_values)) setattr(namespace, self.dest, parsed_values) + IntelliListAction.ltype = ltype return IntelliListAction @@ -109,6 +115,12 @@ def extract_title_author_version(nb): version = version[0] if len(version) else None return title, author, version + +def first_code_cell(nb): + """ Return the first code cell of a notebook """ + for cell in nb.cells: + if cell.cell_type == 'code': + return cell def first_markdown_cell(nb): """ Return the first markdown cell of a notebook """ @@ -139,6 +151,35 @@ def make_epilog(nb, caltype=None): msg += sline + "\n" msg += "\n" return msg + +def get_notebook_function(nb, fname): + import re + flines = [] + def_found = False + indent = None + for cell in nb.cells: + if cell.cell_type == 'code': + lines = cell.source.split("\n") + for line in lines: + + if def_found: + lin = len(line) - len(line.lstrip()) + if indent is None: + if lin != 0: + indent = lin + flines.append(line) + elif lin >= indent: + flines.append(line) + else: + return "\n".join(flines) + + if re.search(r"def\s+{}\(.*\):\s*".format(fname), line) and not def_found: + # print("Found {} in line {}".format(fname, line)) + # set this to indent level + def_found = True + flines.append(line) + return None + # extend the parser according to user input # the first case is if a detector was given, but no calibration type @@ -187,38 +228,85 @@ elif len(sys.argv) >= 3: exit() with open(notebook, "r") as f: nb = nbformat.read(f, as_version=4) - parser.description = make_epilog(nb) - parms = extract_parameters(nb) - for p in parms: - helpstr = ("Default: %(default)s" if not p.comment - else "{}. Default: %(default)s".format(p.comment.replace("#", " ").strip())) - required = p.comment is not None and "required" in p.comment + + ext_func = notebooks[detector][caltype].get("extend parms", None) + + def do_parse(nb, parser, overwrite_reqs=False): + parser.description = make_epilog(nb) + parms = extract_parameters(nb) - if p.type == list: - if len(p.value): - ltype = type(p.value[0]) + for p in parms: + + helpstr = ("Default: %(default)s" if not p.comment + else "{}. Default: %(default)s".format(p.comment.replace("#", " ").strip())) + required = (p.comment is not None and "required" in p.comment) and not overwrite_reqs + + if p.type == list: + if len(p.value): + ltype = type(p.value[0]) + else: + ltype = str + range_allowed = "RANGE ALLOWED" in p.comment.upper() if p.comment else False + parser.add_argument("--{}".format(consolize_name(p.name)), + nargs='+', + type=ltype if not range_allowed else str, + default=p.value if (not required) and p.name != cvar else None, + help=helpstr, + required=required and p.name != cvar, + action=make_intelli_list(ltype) if range_allowed else None) + elif p.type == bool: + parser.add_argument("--{}".format(consolize_name(p.name)), + action="store_true", + default=p.value if not required else None, + help=helpstr, + required=required) else: - ltype = str - range_allowed = "RANGE ALLOWED" in p.comment.upper() if p.comment else False - parser.add_argument("--{}".format(consolize_name(p.name)), - nargs='+', - type=ltype if not range_allowed else str, - default=p.value if (not required) and p.name != cvar else None, - help=helpstr, - required=required and p.name != cvar, - action=make_intelli_list(ltype) if range_allowed else None) - elif p.type == bool: - parser.add_argument("--{}".format(consolize_name(p.name)), - action="store_true", - default=p.value if not required else None, - help=helpstr, - required=required) + parser.add_argument("--{}".format(consolize_name(p.name)), + type=p.type, + default=p.value if not required else None, + help=helpstr, + required=required) + + do_parse(nb, parser, True) + + # extend parameters if needed + ext_func = notebooks[detector][caltype].get("extend parms", None) + if ext_func is not None: + func = get_notebook_function(nb, ext_func) + + if func is None: + warnings.warn("Didn't find concurrency function {} in notebook".format(ext_func), + RuntimeWarning) + else: - parser.add_argument("--{}".format(consolize_name(p.name)), - type=p.type, - default=p.value if not required else None, - help=helpstr, - required=required) + # remove help calls as they will cause the argument parser to exit + add_help = False + if "-h" in sys.argv: + sys.argv.remove("-h") + add_help = True + if "--help" in sys.argv: + sys.argv.remove("--help") + add_help = True + known, remaining = parser.parse_known_args() + if add_help: + sys.argv.append("--help") + args = deconsolize_args(vars(known)) + + df = {} + + exec(func, df) + f = df[ext_func] + import inspect + sig = inspect.signature(f) + callargs = [] + for arg in sig.parameters: + callargs.append(args[arg]) + + extention = f(*callargs) + fcc = first_code_cell(nb) + fcc["source"] += "\n"+extention + parser = make_initial_parser() + do_parse(nb, parser, False) def has_parm(parms, name): @@ -247,6 +335,7 @@ def concurrent_run(temp_path, nb, nbname, args, cparm=None, cval=None, args["cluster_profile"] = "{}_{}".format(args["cluster_profile"], suffix) # first convert the notebook + parms = extract_parameters(nb) params = parameter_values(parms, **args) new_nb = replace_definitions(nb, params, execute=False) base_name = nbname.replace(".ipynb", "") @@ -299,35 +388,6 @@ def concurrent_run(temp_path, nb, nbname, args, cparm=None, cval=None, return jobid -def get_concurrency_function(nb, fname): - import re - flines = [] - def_found = False - indent = None - for cell in nb.cells: - if cell.cell_type == 'code': - lines = cell.source.split("\n") - for line in lines: - - if def_found: - lin = len(line) - len(line.lstrip()) - if indent is None: - if lin != 0: - indent = lin - flines.append(line) - elif lin >= indent: - flines.append(line) - else: - return "\n".join(flines) - - if re.search(r"def\s+{}\(.*\):\s*".format(fname), line): - print("Found {} in line {}".format(fname, line)) - # set this to indent level - def_found = True - flines.append(line) - return None - - def run(): """ Run a calibration task with parser arguments """ @@ -351,7 +411,35 @@ def run(): return with open(notebook, "r") as f: nb = nbformat.read(f, as_version=4) + + # extend parameters if needed + ext_func = notebooks[detector][caltype].get("extend parms", None) + if ext_func is not None: + func = get_notebook_function(nb, ext_func) + + if func is None: + warnings.warn("Didn't find concurrency function {} in notebook".format(ext_func), + RuntimeWarning) + + else: + # remove help calls as they will cause the argument parser to exit + known, remaining = parser.parse_known_args() + args = deconsolize_args(vars(known)) + df = {} + exec(func, df) + f = df[ext_func] + import inspect + sig = inspect.signature(f) + callargs = [] + for arg in sig.parameters: + callargs.append(args[arg]) + + extention = f(*callargs) + fcc = first_code_cell(nb) + fcc["source"] += "\n"+extention + parms = extract_parameters(nb) + title, author, version = extract_title_author_version(nb) if not title: @@ -423,7 +511,7 @@ def run(): cvals = defcval if con_func: - func = get_concurrency_function(nb, con_func) + func = get_notebook_function(nb, con_func) if func is None: warnings.warn("Didn't find concurrency function {} in notebook".format(con_func), RuntimeWarning) diff --git a/xfel_calibrate/notebooks.py b/xfel_calibrate/notebooks.py index 972d6df66269b4596e5821b227a99492b8eb8298..182fbffb6d5effc8f5b68285621838bf32896c32 100644 --- a/xfel_calibrate/notebooks.py +++ b/xfel_calibrate/notebooks.py @@ -12,7 +12,7 @@ notebooks = { "notebook": "notebooks/AGIPD/Chracterize_AGIPD_Gain_PC_NBC.ipynb", "concurrency": {"parameter": "modules", "default concurrency": 16, - "cluster cores": 32}, + "cluster cores": 16}, }, "FF": { "notebook": "notebooks/AGIPD/Characterize_AGIPD_Gain_FlatFields_NBC.ipynb", @@ -74,6 +74,15 @@ notebooks = { "default concurrency": None, "cluster cores": 32}, }, + }, + "GENERIC": { + "DBTOH5": { + "notebook": "notebooks/generic/DB_Constants_to_HDF5_NBC.ipynb", + "concurrency": {"parameter": None, + "default concurrency": None, + "cluster cores": 32}, + "extend parms": "extend_parms", + }, } } \ No newline at end of file