docstr: Applies the analytical FFT on $S$ to generate the diffusion
  propagator. This is calculated on a discrete 3D grid in order to
  obtain an EAP similar to that which is obtained with DSI.
  
  Parameters
  ----------
  gridsize : unsigned int
      dimension of the propagator grid
  radius_max : float
      maximal radius in which to compute the propagator
  
  Returns
  -------
  eap : ndarray
      the ensemble average propagator in the 3D grid
docstr_tok: ["Applies","the","analytical","FFT","on","$","S","$","to","generate","the","diffusion","propagator",".","This","is","calculated","on","a","discrete","3D","grid","in","order","to","obtain","an","EAP","similar","to","that","which","is","obtained","with","DSI",".","Parameters","--","--","--","--","--","gridsize",":","unsigned","int","dimension","of","the","propagator","grid","radius_max",":","float","maximal","radius","in","which","to","compute","the","propagator","Returns","--","--","--","-","eap",":","ndarray","the","ensemble","average","propagator","in","the","3D","grid"]
code: def pdf_grid(self, gridsize, radius_max):
      r"""Applies the analytical FFT on $S$ to generate the diffusion
      propagator. This is calculated on a discrete 3D grid in order to
      obtain an EAP similar to that which is obtained with DSI.
  
      Parameters
      ----------
      gridsize : unsigned int
          dimension of the propagator grid
      radius_max : float
          maximal radius in which to compute the propagator
  
      Returns
      -------
      eap : ndarray
          the ensemble average propagator in the 3D grid
  
      """
      # Create the grid in which to compute the pdf
      rgrid_rtab = self.model.cache_get(
          "pdf_grid", key=(gridsize, radius_max)
      )
      if rgrid_rtab is None:
          rgrid_rtab = create_rspace(gridsize, radius_max)
          self.model.cache_set(
              "pdf_grid", (gridsize, radius_max), rgrid_rtab
          )
      rgrid, rtab = rgrid_rtab
  
      psi = self.model.cache_get(
          "shore_matrix_pdf", key=(gridsize, radius_max)
      )
      if psi is None:
          psi = shore_matrix_pdf(self.radial_order, self.zeta, rtab)
          self.model.cache_set(
              "shore_matrix_pdf", (gridsize, radius_max), psi
          )
  
      propagator = np.dot(psi, self._shore_coef)
      eap = np.empty((gridsize, gridsize, gridsize), dtype=float)
      eap[tuple(rgrid.astype(int).T)] = propagator
      eap *= (2 * radius_max / (gridsize - 1)) ** 3
  
      return eap
  
imports: from warnings import warn
  from math import factorial
  import numpy
  from scipy.special import genlaguerre, gamma, hyp2f1
  from dipy.reconst.cache import Cache
  from dipy.reconst.multi_voxel import multi_voxel_fit
  from dipy.reconst.shm import real_sh_descoteaux_from_index
  from dipy.core.geometry import cart2sphere
  from dipy.utils.optpkg import optional_package
label_desc: Use image node_id 2 for calling the ShoreFit obj's underlying member method code with example usage: obj.pdf_grid(gridsize, radius_max) and returns: eap