{"action":"closed","issue":{"url":"https://api.github.com/repos/spacetelescope/poppy/issues/166","repository_url":"https://api.github.com/repos/spacetelescope/poppy","labels_url":"https://api.github.com/repos/spacetelescope/poppy/issues/166/labels{/name}","comments_url":"https://api.github.com/repos/spacetelescope/poppy/issues/166/comments","events_url":"https://api.github.com/repos/spacetelescope/poppy/issues/166/events","html_url":"https://github.com/spacetelescope/poppy/issues/166","id":353584007,"node_id":"MDU6SXNzdWUzNTM1ODQwMDc=","number":166,"title":"code submission: generate orthonormal basis on arbitrary aperture","user":{"login":"mperrin","id":1151745,"node_id":"MDQ6VXNlcjExNTE3NDU=","avatar_url":"https://avatars0.githubusercontent.com/u/1151745?v=4","gravatar_id":"","url":"https://api.github.com/users/mperrin","html_url":"https://github.com/mperrin","followers_url":"https://api.github.com/users/mperrin/followers","following_url":"https://api.github.com/users/mperrin/following{/other_user}","gists_url":"https://api.github.com/users/mperrin/gists{/gist_id}","starred_url":"https://api.github.com/users/mperrin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mperrin/subscriptions","organizations_url":"https://api.github.com/users/mperrin/orgs","repos_url":"https://api.github.com/users/mperrin/repos","events_url":"https://api.github.com/users/mperrin/events{/privacy}","received_events_url":"https://api.github.com/users/mperrin/received_events","type":"User","site_admin":false},"labels":[],"state":"closed","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":1,"created_at":"2018-08-23T22:58:44Z","updated_at":"2018-08-23T22:58:44Z","closed_at":"2018-08-23T22:58:44Z","author_association":"COLLABORATOR","body":"<a href=\"https://github.com/mperrin\"><img src=\"https://avatars0.githubusercontent.com/u/1151745?v=4\" align=\"left\" width=\"96\" height=\"96\" hspace=\"10\"></img></a> **Issue by [mperrin](https://github.com/mperrin)**\n_Tuesday May 24, 2016 at 13:55 GMT_\n_Originally opened as https://github.com/mperrin/poppy/issues/166_\n\n----\n\nFrom Arthur Vigan at LAM: \n\n> Dear Marshall,\n> \n> I have started using your poppy library, mainly for the implementation of the Zernike polynomials that you have included in the library. In particular, you have written an orthonormalisation process to obtain a basis on an hexagonal aperture, which is super useful! But I think it would be even better to have something for an arbitrary aperture. I recently needed to have Zernikes on a circular aperture with central obscuration, so I implemented a new arbitrary_basis() method (see code below). Please feel free if you think it's a useful addition to the library.\n\n```\n\n####################################################################################\n\ndef arbitrary_basis(aperture, nterms=15, rho=None, theta=None):\n   \"\"\"\n   Return a cube of Zernike terms from 1 to N, calculated on an\n   arbitrary aperture, each as a 2D array showing the value at each\n   point. (Regions outside the unit circle on which the Zernike is\n   defined are initialized to zero.)\n\n   Parameters\n   -----------\n   aperture : array_like\n       2D binary array representing the arbitrary aperture\n   nterms : int, optional\n       Number of Zernike terms to return, starting from piston.\n       (e.g. ``nterms=1`` would return only the Zernike piston term.)\n       Default is 15.\n   rho, theta : array_like, optional\n       Image plane coordinates. `rho` should be 0 at the origin\n       and 1.0 at the edge of the pupil. `theta` should be\n       the angle in radians.\n   \"\"\"\n\n   shape = aperture.shape\n   npix  = shape[0]\n\n   A = aperture.sum()\n\n   # precompute zernikes\n   Z = np.zeros((nterms + 1,) + shape)\n   Z[1:] = zernike_basis(nterms=nterms, npix=npix, rho=rho, theta=theta, outside=0.0)\n\n\n   G = [np.zeros(shape), np.ones(shape)]  # array of G_i etc. intermediate fn\n   H = [np.zeros(shape), np.ones(shape) * aperture]  # array of hexikes\n   c = {}  # coefficients hash\n\n   for j in np.arange(nterms - 1) + 1:  # can do one less since we already have the piston term\n       _log.debug(\"  j = \" + str(j))\n       # Compute the j'th G, then H\n       nextG = Z[j + 1] * aperture\n       for k in np.arange(j) + 1:\n           c[(j + 1, k)] = -1 / A * (Z[j + 1] * H[k] * aperture).sum()\n           if c[(j + 1, k)] != 0:\n               nextG += c[(j + 1, k)] * H[k]\n           _log.debug(\"    c[%s] = %f\", str((j + 1, k)), c[(j + 1, k)])\n\n       nextH = nextG / np.sqrt((nextG ** 2).sum() / A)\n\n       G.append(nextG)\n       H.append(nextH)\n\n       #TODO - contemplate whether the above algorithm is numerically stable\n       # cf. modified gram-schmidt algorithm discussion on wikipedia.\n\n   # drop the 0th null element, return the rest\n   return H[1:]\n```\n\n"}}
{"actor":{"display_login":"mperrin"}}