docs: def calc_empirical_retinotopy(cortex,
                                polar_angle=None, eccentricity=None, pRF_radius=None, weight=None,
                                eccentricity_range=None, weight_min=0,
                                invert_rh_angle=False,
                                partial_voluming_correction=False):
      '''
      calc_empirical_retinotopy computes the value empirical_retinotopy, which is an itable object
        storing the retinotopy data for the registration.
  
      Required afferent parameters:
        @ cortex Must be the cortex object that is to be registered to the model of retinotopy.
   
      Optional afferent parameters:
        @ polar_angle May be an array of polar angle values or a polar angle property name; if None
          (the default), attempts to auto-detect an empirical polar angle property.
        @ eccentricity May be an array of eccentricity values or an eccentricity property name; if
          None (the default), attempts to auto-detect an empirical eccentricity property.
        @ pRF_radius May be an array of receptive field radius values or the property name for such an
          array; if None (the default), attempts to auto-detect an empirical radius property.
        @ weight May be an array of weight values or a weight property name; if None (the default),
          attempts to auto-detect an empirical weight property, such as variance_explained.
        @ eccentricity_range May be a maximum eccentricity value or a (min, max) eccentricity range
          to be used in the registration; if None, then no clipping is done.
        @ weight_min May be given to indicate that weight values below this value should not be
          included in the registration; the default is 0.
        @ partial_voluming_correction May be set to True (default is False) to indicate that partial
          voluming correction should be used to adjust the weights.
        @ invert_rh_angle May be set to True (default is False) to indicate that the right hemisphere
          has its polar angle stored with opposite sign to the model polar angle.
  
      Efferent values:
        @ empirical_retinotopy Will be a pimms itable of the empirical retinotopy data to be used in
          the registration; the table's keys will be 'polar_angle', 'eccentricity', and 'weight';
          values that should be excluded for any reason will have 0 weight and undefined angles.
      '''
      data = {}  # the map we build up in this function
      n = cortex.vertex_count
      (emin,emax) = (-np.inf,np.inf)       if eccentricity_range is None          else \
                    (0,eccentricity_range) if pimms.is_number(eccentricity_range) else \
                    eccentricity_range
      # Step 1: get our properties straight ##########################################################
      (ang, ecc, rad, wgt) = [
          np.array(extract_retinotopy_argument(cortex, name, arg, default='empirical'))
          for (name, arg) in [
                  ('polar_angle', polar_angle),
                  ('eccentricity', eccentricity),
                  ('radius', pRF_radius),
                  ('weight', np.full(n, weight) if pimms.is_number(weight) else weight)]]
      if wgt is None: wgt = np.ones(len(ecc))
      bad = np.logical_not(np.isfinite(np.prod([ang, ecc, wgt], axis=0)))
      ecc[bad] = 0
      wgt[bad] = 0
      if rad is not None: rad[bad] = 0
      # do partial voluming correction if requested
      if partial_voluming_correction: wgt = wgt * (1 - cortex.partial_voluming_factor)
      # now trim and finalize
      bad = bad | (wgt <= weight_min) | (ecc < emin) | (ecc > emax)
      wgt[bad] = 0
      ang[bad] = 0
      ecc[bad] = 0
      for x in [ang, ecc, wgt, rad]:
          if x is not None:
              x.setflags(write=False)
      # that's it!
      dat = dict(polar_angle=ang, eccentricity=ecc, weight=wgt)
      if rad is not None: dat['radius'] = rad
      return (pimms.itable(dat),)