docs: def register_retinotopy(hemi,
                          model='benson17', model_hemi=Ellipsis,
                          polar_angle=None, eccentricity=None, weight=None, pRF_radius=None,
                          weight_min=0.1,
                          eccentricity_range=None,
                          partial_voluming_correction=False,
                          radius_weight=1, field_sign_weight=1, invert_rh_field_sign=False,
                          scale=20.0,
                          sigma=Ellipsis,
                          select='close',
                          prior=None,
                          resample=Ellipsis,
                          radius=np.pi/3,
                          max_steps=2000, max_step_size=0.05, method='random',
                          yield_imap=False):
      '''
      register_retinotopy(hemi) registers the given hemisphere object, hemi, to a model of V1, V2,
        and V3 retinotopy, and yields a copy of hemi that is identical but additionally contains
        the registration 'retinotopy', whose coordinates are aligned with the model.
  
      Registration attempts to align the vertex positions of the hemisphere's spherical surface with a
      model of polar angle and eccentricity. This alignment proceeds through several steps and can
      be modified by several options. A description of these steps and options are provided here. For
      most cases, the default options should work relatively well.
  
      Method:
        (1) Prepare for registration by several intitialization substeps:
              a. Extract the polar angle, eccentricity and weight data from the hemisphere. These
                 data are usually properties on the mesh and can be modifies by the options
                 polar_angle, eccentricity, and weight, which can be either property names or list
                 of property values. By default (None), a property is chosen using the functions
                 neuropythy.vision.extract_retinotopy_argument with the default option set to
                 'empirical'.
              b. If partial voluming correction is enabled (via the option
                 partial_voluming_correction), multiply the weight by (1 - p) where p is 
                 hemi.partial_volume_factor.
              c. If there is a prior that is specified as a belief about the retinotopy, then a
                 Registration is created for the hemisphere such that its vertices are arranged
                 according to that prior (see also the prior option). Note that because hemi's
                 coordinates must always be projected into the registration specified by the model,
                 the prior must be the name of a registration to which the model's specified subject
                 is also registered. This is clear in the case of an example. The default value for
                 this is 'retinotopy'; assuming that our model is specified on the fsaverage_sym, 
                 surface, the initial positions of the coordinates for the registration process would
                 be the result of starting with hemi's fsaverage_sym-aligned coordinates then warping
                 these coordinates in a way that is equivalent to the warping from fsaverage_sym's 
                 native spherical coordinates to fsaverage_sym's retinotopy registration coordinates.
                 Note that the retinotopy registration would usually be specified in a file in the
                 fsaverage_sym subject's surf directory: surf/lh.retinotopy.sphere.reg.
                 If no prior is specified (option value None), then the vertices that are used are
                 those aligned with the registration of the model, which will usually be 'fsaverage'
                 or 'fsaverage_sym'.
              d. If the option resample is not None, then the vertex coordinates are resampled onto
                 either the fsaverage or fsaverage_sym's native sphere surface. (The value of resample
                 should be either 'fsaverage' or 'fsaverage_sym'.) Resampling can prevent vertices
                 that have been rearranged by alignment with the model's specified registration or by
                 application of a prior from beginning the alignment with very high initial gradients
                 and is recommended for subject alignments.
                 If resample is None then no changes are made.
              e. A 2D projection of the (possibly aligned, prior-warped, and resampled) cortical
                 surface is made according to the projection parameters of the model. This map is the
                 mesh that is warped to eventually fit the model.
        (2) Perform the registration by running neuropythy.registration.mesh_register. This step
            consists of two major components.
              a. Create the potential function, which we will minimize. The potential function is a
                 complex function whose inputs are the coordinates of all of the vertices and whose
                 output is a potential value that increases both as the mesh is warped and as the
                 vertices with retinotopy predictions get farther away from the positions in the model
                 that their retinotopy values would predict they should lie. The balance of these
                 two forces is best controlled by the option functional_scale. The potential function
                 fundamentally consists of four terms; the first three describe mesh deformations and
                 the last describes the model fit.
                  - The edge deformation term is described for any vertices u and v that are connected
                    by an edge in the mesh; it's value is c/p (r(u,v) - r0(u,v))^2 where c is the
                    edge_scale, p is the number of edges in the mesh, r(a,b) is the distance between
                    vertices a and b, and r0(a,b) is the distance between a and b in the initial mesh.
                  - The angle deformation term is described for any three vertices (u,v,w) that form
                    an angle in the mesh; its value is c/m h(t(u,v,w), t0(u,v,w)) where c is the
                    angle_scale argument, m is the number of angles in the mesh, t is the value of the
                    angle (u,v,w), t0 is the value of the angle in the initial mesh, and h(t,t0) is an
                    infinite-well function that asymptotes to positive infinity as t approaches both 0
                    and pi and is minimal when t = t0 (see the nben's 
                    nben.mesh.registration.InfiniteWell documentation for more details).
                  - The perimeter term prevents the perimeter vertices form moving significantly;
                    this primarily prevents the mesh from wrapping in on itself during registration.
                    The form of this term is, for any vertex u on the mesh perimeter, 
                    (x(u) - x0(u))^2 where x and x0 are the position and initial position of the
                    vertex.
                  - Finally, the functional term is minimized when the vertices best align with the
                    retinotopy model.
              b. Register the mesh vertices to the potential function using the nben Java library. The
                 particular parameters of the registration are method, max_steps, and max_step_size.
  
      Options:
        * model specifies the instance of the retinotopy model to use; this must be an
          instance of the RegisteredRetinotopyModel class or a string that can be passed to the
          retinotopy_model() function (default: 'standard').
        * model_hemi specifies the hemisphere of the model; generally you shouldn't have to set this
          unless you are using an fsaverage_sym model, in which case it should be set to None; in all
          other cases, the default value (Ellipsis) instructs the function to auto-detect the
          hemisphere.
        * polar_angle, eccentricity, pRF_radius, and weight specify the property names for the
          respective quantities; these may alternately be lists or numpy arrays of values. If weight
          is not given or found, then unity weight for all vertices is assumed. By default, each will
          check the  hemisphere's properties for properties with compatible names; it will prefer the
          properties PRF_polar_angle, PRF_ecentricity, and PRF_variance_explained if possible.
        * weight_min (default: 0.1) specifies the minimum value a vertex must have in the weight
          property in order to be considered as retinotopically relevant.
        * eccentricity_range (default: None) specifies that any vertex whose eccentricity is too low
          or too high should be given a weight of 0 in the registration.
        * partial_voluming_correction (default: True), if True, specifies that the value
          (1 - hemi.partial_volume_factor) should be applied to all weight values (i.e., weights
          should be down-weighted when likely to be affected by a partial voluming error).
        * field_sign_weight (default: 1) indicates the relative weight (between 0 and 1) that should
          be given to the field-sign as a method of determining which anchors have the strongest
          springs. A value of 1 indicates that the effective weights of anchors should be the 
          geometric mean of the empirical retinotopic weight and field-sign-based weight; a value of 0
          indicates that no attention should be paid to the field sign weight.
        * radius_weight (default: 1) indicates the relative weight (between 0 and 1) that should
          be given to the pRF radius as a method of determining which anchors have the strongest
          springs. A value of 1 indicates that the effective weights of anchors should be the 
          geometric mean of the empirical retinotopic weight and pRF-radius-based weight; a value of 0
          indicates that no attention should be paid to the radius-based weight.
        * sigma specifies the standard deviation of the Gaussian shape for the Schira model anchors;
          see retinotopy_anchors for more information.
        * scale (default: 1.0) specifies the strength of the functional constraints (i.e. the anchors:
          the part of the minimization responsible for ensuring that retinotopic coordinates are
          aligned); the anatomical constraints (i.e. the edges and angles: the part of the
          minimization responsible for ensuring that the mesh is not overly deformed) are always held
          at a strength of 1.0.
        * select specifies the select option that should be passed to retinotopy_anchors.
        * max_steps (default 2,000) specifies the maximum number of registration steps to run. This
          may be a tuple (max_steps, stride) in which case the registered map that is returned will
          contain a piece of meta-data, 'trajectory' containing the vertex coordinates every stride
          steps of the registration.
        * max_step_size (default 0.05) specifies the maxmim distance a single vertex is allowed to
          move in a single step of the minimization.
        * method (default 'random') is the method argument passed to mesh_register. This should be
          'random', 'pure', or 'nimble'. Generally, 'random' is recommended.
        * yield_imap (default: False) specifies whether the return value should be the new
          Mesh object or a pimms imap (i.e., a persistent mapping of the result of a pimms
          calculation) containing the meta-data that was used during the registration
          calculations. If this is True, then register_retinotopy will return immediately, and
          calculations will only be performed as the relevant data are requested from the returned
          imap. The item 'predicted_mesh' gives the return value when yield_imap is set to False.
        * radius (default: pi/3) specifies the radius, in radians, of the included portion of the map
          projection (projected about the occipital pole).
        * sigma (default Ellipsis) specifies the sigma argument to be passed onto the 
          retinotopy_anchors function (see help(retinotopy_anchors)); the default value, Ellipsis,
          is interpreted as the default value of the retinotopy_anchors function's sigma option.
        * prior (default: None) specifies the prior that should be used, if found, in the 
          topology registrations for the subject associated with the retinotopy_model's registration.
        * resample (default: Ellipsis) specifies that the data should be resampled to one of
          the uniform meshes, 'fsaverage' or 'fsaverage_sym', prior to registration; if None then no
          resampling is performed; if Ellipsis, then auto-detect either fsaverage or fsaverage_sym
          based on the model_hemi option (if it is None, fsaverage_sym, else fsaverage).
      '''
      # create the imap
      m = retinotopy_registration(
          cortex=hemi, model_argument=model, model_hemi=model_hemi,
          polar_angle=polar_angle, eccentricity=eccentricity, weight=weight, pRF_radius=pRF_radius,
          weight_min=weight_min,  eccentricity_range=eccentricity_range,
          partial_voluming_correction=partial_voluming_correction,
          radius_weight=radius_weight, field_sign_weight=field_sign_weight,
          invert_rh_field_sign=invert_rh_field_sign,
          scale=scale, sigma=sigma, select=select, prior=prior, resample=resample, radius=radius,
          max_steps=max_steps, max_step_size=max_step_size, method=method)
      return m if yield_imap else m['predicted_mesh']