docs: def set(self, *args, **kwargs):
          '''
          d.set(...) yields a copy of the IMap object d; the ... may be replaced with either
          nothing (in which case d is returned) or a list of 0 or more dictionaries followed by a lsit
          of zero or more keyword arguments. These dictionaries and keywords arguments are merged
          left-to-right; the result may contain only afferent parameters of d and replaces the values
          of d in the newly returned calc dictionary.
          '''
          args = merge(args, kwargs)
          if len(args) == 0: return self
          affs = self.afferents
          pln = self.plan
          # make sure these are all valid parameters
          if any(k not in affs for k in six.iterkeys(args)):
              raise TypeError(
                  'The given key \'%s\' is not an afferent parameter of IMap object')
          # okay, we can make the change...
          new_calc_dict = copy.copy(self)
          new_affs = reduce(lambda m,v: m.set(v[0],v[1]), six.iteritems(args), affs)
          object.__setattr__(new_calc_dict, 'afferents', new_affs)
          # we need to run checks and delete any cache that has been invalidated.
          # The calculation's check method does this; it raises an exception if there is an error
          pln._check(new_calc_dict, changes=args)
          return new_calc_dict