def _test_cross_zernikes(testj=4, nterms=10, npix=500): """Verify the functions are orthogonal, by taking the integrals of a given Zernike times N other ones. Parameters : -------------- testj : int Index of the Zernike polynomial to test against the others nterms : int Test that polynomial against those from 1 to this N npix : int Size of array to use for this test """ zj = zernike.zernike1(testj, npix=npix) assert np.sum( np.isfinite(zj)) > 0, "Zernike calculation failure; all NaNs." zbasis = zernike.zernike_basis(nterms=nterms, npix=npix) for idx, z in enumerate(zbasis): j = idx + 1 if j == testj or j == 1: continue # discard piston term and self prod = z * zj wg = np.where(np.isfinite(prod)) cross_sum = np.abs(prod[wg].sum()) assert cross_sum < 1e-9, ( "orthogonality failure, Sum[Zernike(j={}) * Zernike(j={})] = {} (> 1e-9)" .format(j, testj, cross_sum))
def test_cached_zernike1(nterms=10): radius = 1.1 osys = poppy_core.OpticalSystem() osys.addPupil(optics.CircularAperture(radius=radius)) wave = osys.inputWavefront() y, x = wave.coordinates() rho = np.sqrt(y ** 2 + x ** 2) / radius theta = np.arctan2(y, x) cached_results = [] for j in range(1, nterms + 1): cached_output = zernike.cached_zernike1(j, wave.shape, wave.pixelscale, radius, outside=0.0) cached_results.append(cached_output) uncached_output = zernike.zernike1(j, rho=rho, theta=theta, outside=0.0) assert np.allclose(cached_output, uncached_output) try: cached_output[0, 0] = np.nan assert False, "Shouldn't be able to assign to a cached output array!" except ValueError: pass # Check that we're getting cached copies for j, array_ref in enumerate(cached_results, start=1): cached_array_ref = zernike.cached_zernike1(j, wave.shape, wave.pixelscale, radius, outside=0.0) assert id(array_ref) == id(cached_array_ref), "cached_zernike1 returned a new object for the same arguments"
def _test_cross_zernikes(testj=4, nterms=10, npix=500): """Verify the functions are orthogonal, by taking the integrals of a given Zernike times N other ones. Parameters : -------------- testj : int Index of the Zernike polynomial to test against the others nterms : int Test that polynomial against those from 1 to this N npix : int Size of array to use for this test """ Zj = zernike.zernike1(testj, npix=npix) Zbasis = zernike.zernike_basis(nterms=nterms, npix=npix) for idx, Z in enumerate(Zbasis): j = idx + 1 if j == testj or j == 1: continue # discard piston term and self prod = Z * Zj wg = np.where(np.isfinite(prod)) cross_sum = np.abs(prod[wg].sum()) assert cross_sum < 1e-9, ( "orthogonality failure, Sum[Zernike(j={}) * Zernike(j={})] = {} (> 1e-9)".format( j, testj, cross_sum) )
def test_cached_zernike1(nterms=10): radius = 1.1 osys = poppy_core.OpticalSystem() osys.add_pupil(optics.CircularAperture(radius=radius)) wave = osys.input_wavefront() y, x = wave.coordinates() rho = np.sqrt(y ** 2 + x ** 2) / radius theta = np.arctan2(y, x) cached_results = [] for j in range(1, nterms + 1): cached_output = zernike.cached_zernike1(j, wave.shape, wave.pixelscale, radius, outside=0.0) cached_results.append(cached_output) uncached_output = zernike.zernike1(j, rho=rho, theta=theta, outside=0.0) assert np.allclose(cached_output, uncached_output) try: cached_output[0, 0] = np.nan assert False, "Shouldn't be able to assign to a cached output array!" except ValueError: pass # Check that we're getting cached copies for j, array_ref in enumerate(cached_results, start=1): cached_array_ref = zernike.cached_zernike1(j, wave.shape, wave.pixelscale, radius, outside=0.0) assert id(array_ref) == id(cached_array_ref), "cached_zernike1 returned a new object for the same arguments"
def test_zernikes_rms(nterms=10, size=500): """Verify RMS(Zernike[n,m]) == 1.""" assert np.nanstd(zernike.zernike1(1)) == 0.0, "Zernike(j=0) has nonzero RMS" for j in range(2, nterms): n, m = zernike.noll_indices(j) z = zernike.zernike(n, m, npix=size) rms = np.nanstd(z) # exclude masked pixels assert abs(1.0 - rms) < 0.001, "Zernike(j={}) has RMS value of {}".format(j, rms)
def test_zernikes_rms(nterms=10, size=500): """Verify RMS(Zernike[n,m]) == 1.""" assert np.nanstd(zernike.zernike1(1)) == 0.0, "Zernike(j=0) has nonzero RMS" for j in range(2, nterms): n, m = zernike.noll_indices(j) Z = zernike.zernike(n, m, npix=size) rms = np.nanstd(Z) # exclude masked pixels assert 1.0 - rms < 0.001, "Zernike(j={}) has RMS value of {}".format(j, rms)
def test_ones_zernikes(nterms=10): """Verify the radial scaling function is correctly normalized""" rho = np.ones(3) theta = np.array([0, 1, 2]) for j in np.arange(nterms)+1: n, m = zernike.noll_indices(j) Z = zernike.zernike1(j, rho=rho, theta=theta) Rs = zernike.R(n, m, rho) print "j=%d\tZ_(%d,%d) [1] = \t %s" % (j, n, m, str(Rs)) assert Rs[0] == Rs[1] == Rs[2], "Radial polynomial is not radially symmetric"
def zern_seg(self, segment, vmax=150, unit='nm'): """ Show the Zernike terms applied to a given segment""" # the actual wavefront is always in units of microns. if unit == 'nm': scalefact = 1000. elif unit =='micron': scalefact = 1.0 else: raise ValueError("unknown unit keyword") nzerns = 11 title = "Zernike components for "+segment zerns = np.zeros((nzerns)) if segment+"-decenter" in self.state.keys(): zerns += self._move(segment, type='decenter', vector=self.state[segment+"-decenter"], return_zernikes=True) title += ", displacement=[%.2e, %.2e, %.2e] um" % tuple(self.state[segment+"-decenter"]) if segment+"-tilt" in self.state.keys(): zerns += self._move(segment, type='tilt', vector=self.state[segment+"-tilt"], return_zernikes=True) title += ", tilts =[%.5f, %.5f, %.5f] urad" % tuple(self.state[segment+"-tilt"]) _log.info("Zerns: "+str(zerns)) fig = plt.gcf() fig.clf() npix=200 hexap = zernike.hex_aperture(npix) hexap[np.where(hexap == 0)] = np.nan cmap = matplotlib.cm.jet cmap.set_bad('0.5', alpha=0.0) for j in np.arange(nzerns)+1: ax = fig.add_subplot(3, 4, j, frameon=False, xticks=[], yticks=[]) # n, m = zernike.noll_indices(j) Z = zernike.zernike1(j, npix=npix) ax.imshow(Z * zerns[j-1] * hexap*scalefact, vmin=-1*vmax, vmax=vmax, cmap=cmap) ax.text(npix*0.95, npix*0.8, "$Z%d$" % (j), fontsize=20, horizontalalignment='right') ax.text(npix*0.95, npix*0.1, "%.2e" % (zerns[j-1]), fontsize=15, horizontalalignment='right') fig.text(0.5, 0.95, title, horizontalalignment='center', fontsize=15) fig.text(0.95, 0.15, 'Segment RMS WFE = %.2f %s ' % (self.rms(segment)*(scalefact/1000), unit), fontsize=10, horizontalalignment='right') #fig.text(0.95, 0.05, 'OPDs scaled from %.2e - %.2e um' % (-vmax, vmax), horizontalalignment='right', fontsize=10) fig.text(0.95, 0.05, 'OPDs scaled from %.2f - %.2f %s' % (-vmax, vmax, unit), horizontalalignment='right', fontsize=10) plt.draw()
def _apply_zernikes_to_seg(self, segment, zernike_coeffs): """ Apply Zernike perturbations to a given segment """ assert(segment in self.segnames) iseg = np.where(self.segnames == segment)[0][0]+1 # segment index from 1 - 18 wseg = np.where(self._segment_masks == iseg) # determine the X and Y zernike coefficients for each segment # determine the center of each segment, as the mean of the min and max X and Y values Y, X = np.indices(self._opdHDU.data.shape) cx = np.mean([X[wseg].min(), X[wseg].max()]) cy = np.mean([Y[wseg].min(), Y[wseg].max()]) seg_radius = (X[wseg].max()-X[wseg].min())/2.0 _log.debug("Segment %s is centered at pixel loc (%.1f, %.1f) with radius %.1f pix" % (segment, cx, cy, seg_radius)) Xw = X[wseg].astype(np.float64) Yw = Y[wseg].astype(np.float64) Yw -= cy Xw -= cx ang = self._rotations[segment]*np.pi/180 # This definitely has to be a positive sign, # to get the right X, Y for locally-defined zernikes Xr = Xw * np.cos(ang) + Yw * np.sin(ang) Yr = Xw *-np.sin(ang) + Yw * np.cos(ang) theta = np.arctan2(Yr, Xr) Rw = np.sqrt(Xr**2 + Yr**2)/seg_radius #zernike_coeffs *= 0 #zernike_coeffs[2]=1 if self.remove_piston_tip_tilt: zernike_coeffs[0:3] = 0 for i in range(len(zernike_coeffs)): zern = zernike.zernike1(i+1, rho=Rw, theta=theta, outside=0.0) * zernike_coeffs[i] #print "Z%d = %f" % (i+1, zernike_coeffs[i]) self._opdHDU.data[wseg] += zern #self._opdHDU.data[wseg] = Yw outtxt="Zs=["+", ".join(['%.1e'%z for z in zernike_coeffs])+"]" #print outtxt _log.debug(" "+outtxt)