docs: def addResult( self, result, jobids = None ):
          """Add a result. This should be :term:`results dict` as returned from
          an instance of :class:`Experiment`, that contains metadata,
          parameters, and result. Results cannot be overridden, as
          notebooks are immutable: adding more results simply adds
          another result set.
  
          The results may include one or more nested results dicts, for example as
          returned by :meth:`RepeatedExperiment`, whose results are a list of results
          at the same point in the parameter space. In this case the emebedded
          results will themselves be unpacked and added.
  
          One may also add a list of results dicts directly, in which case they will
          be added individually.
  
          If the jobid is present, this result resolves the corresponding pending result.
          As with result, jobid can be a list. (The two lists need not be the same
          length, to allow for experiments that return lists of result dicts.)
  
          :param result: a results dict
          :param jobids: the pending result job id(s) (defaults to no jobs)
          """
  
          # deal with the dufferent ways of presenting results to be added
          if isinstance(result, list):
              # a list, recursively add
              for res in result:
                  self.addResult(res)
          else:
              if isinstance(result, dict):
                  if isinstance(result[Experiment.RESULTS], list):
                      # a result with embedded results, unwrap and and add them
                      for res in result[Experiment.RESULTS]:
                          self.addResult(res)
                  else:
                      # a single results dict with a single set of experimental results
                      k = self._parametersAsIndex(result[Experiment.PARAMETERS])
              
                      # retrieve or create the result list
                      if k in self._results.keys():
                          rs = self._results[k]
                      else:
                          rs = []
                          self._results[k] = rs
  
                      # store the result
                      rs.insert(0, result)
              else:
                  raise Exception("Can't deal with results like this: {r}".format(r = result)) 
                      
          # if there is are job ids provided, cancel the corresponding pending jobs
          if jobids is not None:
              if not isinstance(jobids, list):
                  jobids = [ jobids ]
              for jobid in jobids:
                  if jobid in self._pending.keys():
                      # grab the results list for which this is a pending job
                      k = self._pending[jobid]
                      if k in self._results.keys():
                          # delete job id from current results
                          rs = self._results[k]
                          j = rs.index(jobid)
                          del rs[j]
                      
                          # ...and from the set of pending results
                          del self._pending[jobid]
                      else:
                          # we've screwed-up the internal data structures
                          raise RuntimeError('Internal structure error for {j} -> {ps}'.format(j = jobid,
                                                                                               ps = k))
                  else:
                      # we've screwed-up the internal data structures
                      raise RuntimeError('Internal structure error for {j}'.format(j = jobid))