docs: def write(path_or_h5file, data={}, meta={}, logs={}, mode="reset",
            compression=None):
      """Write data to an RT-DC file
  
      Parameters
      ----------
      path: path or h5py.File
          The path or the hdf5 file object to write to.
      data: dict-like
          The data to store. Each key of `data` must be a valid
          feature name (see `dclab.dfn.feature_names`). The data
          type must be given according to the feature type:
  
          - scalar feature: 1d ndarray of size `N`, any dtype,
            with the number of events `N`.
          - contour: list of `N` 2d ndarrays of shape `(2,C)`, any dtype,
            with each ndarray containing the x- and y- coordinates
            of `C` contour points in pixels.
          - image: 3d ndarray of shape `(N,A,B)`, is converted to uint8,
            with the image dimensions `(x,y) = (A,B)`
          - mask: 3d ndarray of shape `(N,A,B)`, is converted to bool,
            with the image dimensions `(x,y) = (A,B)`
          - trace: 2d ndarray of shape `(N,T)`, any dtype
            with a globally constant trace length `T`.
      meta: dict of dicts
          The meta data to store (see `dclab.dfn.config_keys`).
          Each key depicts a meta data section name whose data is given
          as a dictionary, e.g.
  
              meta = {"imaging": {"exposure time": 20,
                                  "flash duration": 2,
                                  ...
                                  },
                      "setup": {"channel width": 20,
                                "chip region": "channel",
                                ...
                                },
                      ...
                      }
  
          Only section key names and key values therein registered
          in dclab are allowed and are converted to the pre-defined
          dtype.
      logs: dict of lists
          Each key of `logs` refers to a list of strings that contains
          logging information. Each item in the list can be considered to
          be one line in the log file.
      mode: str
          Defines how the input `data` and `logs` are stored:
          - "append": append new data to existing Datasets; the opened
                      `h5py.File` object is returned (used in real-
                      time data storage)
          - "replace": replace keys given by `data` and `logs`; the
                      opened `h5py.File` object is closed and `None`
                      is returned (used for ancillary feature storage)
          - "reset": do not keep any previous data; the opened
                     `h5py.File` object is closed and `None` is returned
                     (default)
      compression: str
          Compression method for "contour", "image", and "trace" data
          as well as logs; one of [None, "lzf", "gzip", "szip"].
  
      Notes
      -----
      If `data` is an instance of RTDCBase, then `meta` must be set to
      `data.config`, otherwise no meta data will be saved.
      """
      if mode not in ["append", "replace", "reset"]:
          raise ValueError("`mode` must be one of [append, replace, reset]")
      if compression not in [None, "gzip", "lzf", "szip"]:
          raise ValueError("`compression` must be one of [gzip, lzf, szip]")
  
      if (not hasattr(data, "__iter__") or
          not hasattr(data, "__contains__") or
              not hasattr(data, "__getitem__") or
              isinstance(data, (list, np.ndarray))):
          msg = "`data` must be dict-like"
          raise ValueError(msg)
  
      # Check meta data
      for sec in meta:
          if sec not in dfn.config_keys:
              # only allow writing of meta data that are not editable
              # by the user (not dclab.dfn.CFG_ANALYSIS)
              msg = "Meta data section not defined in dclab: {}".format(sec)
              raise ValueError(msg)
          for ck in meta[sec]:
              if ck not in dfn.config_keys[sec]:
                  msg = "Meta key not defined in dclab: {}:{}".format(sec, ck)
                  raise ValueError(msg)
  
      # Check feature keys
      feat_keys = []
      for kk in data:
          if kk in dfn.feature_names:
              feat_keys.append(kk)
          else:
              raise ValueError("Unknown key '{}'!".format(kk))
          # verify trace names
          if kk == "trace":
              for sk in data["trace"]:
                  if sk not in dfn.FLUOR_TRACES:
                      msg = "Unknown trace key: {}".format(sk)
                      raise ValueError(msg)
  
      # Create file
      # (this should happen after all checks)
      if isinstance(path_or_h5file, h5py.File):
          h5obj = path_or_h5file
      else:
          if mode == "reset":
              h5mode = "w"
          else:
              h5mode = "a"
          h5obj = h5py.File(path_or_h5file, mode=h5mode)
  
      # update version
      # - if it is not already in the hdf5 file (prevent override)
      # - if it is explicitly given in meta (append to old version string)
      if ("setup:software version" not in h5obj.attrs
              or ("setup" in meta and "software version" in meta["setup"])):
          thisver = "dclab {}".format(version)
          if "setup" in meta and "software version" in meta["setup"]:
              oldver = meta["setup"]["software version"]
              thisver = "{} | {}".format(oldver, thisver)
          if "setup" not in meta:
              meta["setup"] = {}
          meta["setup"]["software version"] = thisver
      # Write meta
      for sec in meta:
          for ck in meta[sec]:
              idk = "{}:{}".format(sec, ck)
              conffunc = dfn.config_funcs[sec][ck]
              h5obj.attrs[idk] = conffunc(meta[sec][ck])
  
      # Write data
      # create events group
      if "events" not in h5obj:
          h5obj.create_group("events")
      events = h5obj["events"]
      # remove previous data
      if mode == "replace":
          for rk in feat_keys:
              if rk in events:
                  del events[rk]
      # store experimental data
      for fk in feat_keys:
          if fk in dfn.scalar_feature_names:
              store_scalar(h5group=events,
                           name=fk,
                           data=data[fk])
          elif fk == "contour":
              store_contour(h5group=events,
                            data=data["contour"],
                            compression=compression)
          elif fk == "image":
              store_image(h5group=events,
                          data=data["image"],
                          compression=compression)
          elif fk == "mask":
              store_mask(h5group=events,
                         data=data["mask"],
                         compression=compression)
          elif fk == "trace":
              store_trace(h5group=events,
                          data=data["trace"],
                          compression=compression)
  
      # Write logs
      if "logs" not in h5obj:
          h5obj.create_group("logs")
      log_group = h5obj["logs"]
      # remove previous data
      if mode == "replace":
          for rl in logs:
              if rl in log_group:
                  del log_group[rl]
      dt = h5py.special_dtype(vlen=hdf5_str)
      for lkey in logs:
          ldata = logs[lkey]
          if isinstance(ldata, (str, hdf5_str)):
              # single event
              ldata = [ldata]
          lnum = len(ldata)
          if lkey not in log_group:
              log_dset = log_group.create_dataset(lkey,
                                                  (lnum,),
                                                  dtype=dt,
                                                  maxshape=(None,),
                                                  chunks=True,
                                                  fletcher32=True,
                                                  compression=compression)
              for ii, line in enumerate(ldata):
                  log_dset[ii] = line
          else:
              log_dset = log_group[lkey]
              oldsize = log_dset.shape[0]
              log_dset.resize(oldsize + lnum, axis=0)
              for ii, line in enumerate(ldata):
                  log_dset[oldsize + ii] = line
  
      if mode == "append":
          return h5obj
      else:
          h5obj.close()
          return None