def focalMmToCcdPixel(self, focalPlaneX, focalPlaneY): r"""Given focal plane position return the detector position Parameters ---------- focalPlaneX : `float` The x-focal plane position (mm) relative to the centre of the camera focalPlaneY : `float` The y-focal plane position (mm) relative to the centre of the camera N.b. all pixel coordinates have the centre of the bottom-left pixel at (0.0, 0.0) and CCD columns are taken to be vertical, with "R00" in the bottom left of the image Returns ------- detectorName : `str` The name of the detector ccdX : `int` The column pixel position relative to the corner of the detector ccdY : `int` The row pixel position relative to the corner of the detector Raises ------ RuntimeError If the requested position doesn't lie on a detector """ detector, ccdXY = focalMmToCcdPixel(self.camera, geom.PointD(focalPlaneX, focalPlaneY)) ccdX, ccdY = ccdXY return detector.getName(), ccdX, ccdY
def ccdPixelToAmpPixel(self, ccdX, ccdY, detectorName=None): r"""Given position within a detector, return the amplifier position Parameters ---------- ccdX : `int` column pixel position within detector ccdY : `int` row pixel position within detector detectorName : `str` Name of detector (or default from setDetectorName() if None) N.b. all pixel coordinates have the centre of the bottom-left pixel at (0.0, 0.0) Returns ------- channel: `int` Channel number of amplifier (1-indexed; identical to HDU) ampX : `int` The column coordinate relative to the corner of the single-amp image ampY : `int` The row coordinate relative to the corner of the single-amp image Raises ------ RuntimeError If the requested pixel doesn't lie on the detector """ amp, ampXY = ccdPixelToAmpPixel(geom.PointD(ccdX, ccdY), self.getDetector(detectorName)) ampX, ampY = ampXY return ampToChannel(amp), ampX, ampY
def testSwap(self): x00, y00, x01, y01 = (0., 1., 2., 3.) x10, y10, x11, y11 = (4., 5., 6., 7.) box0 = geom.Box2D(geom.PointD(x00, y00), geom.PointD(x01, y01)) box1 = geom.Box2D(geom.PointD(x10, y10), geom.PointD(x11, y11)) box0.swap(box1) self.assertEqual(box0.getMinX(), x10) self.assertEqual(box0.getMinY(), y10) self.assertEqual(box0.getMaxX(), x11) self.assertEqual(box0.getMaxY(), y11) self.assertEqual(box1.getMinX(), x00) self.assertEqual(box1.getMinY(), y00) self.assertEqual(box1.getMaxX(), x01) self.assertEqual(box1.getMaxY(), y01)
def makeitLsst(prefs, context, saveWcs=False, plot=dict()): """This is the python wrapper that reads lsst tables """ # Create an array of PSFs (one PSF for each extension) if prefs.getVerboseType() != prefs.QUIET: print("----- %d input catalogues:" % prefs.getNcat()) if saveWcs: # only needed for making plots wcssList = [] fields = psfexLib.vectorField() for cat in prefs.getCatalogs(): field = psfexLib.Field(cat) wcss = [] wcssList.append(wcss) with fits.open(cat): # Hack: I want the WCS so I'll guess where the calexp is to be # found calexpFile = guessCalexp(cat) md = readMetadata(calexpFile) wcs = afwGeom.makeSkyWcs(md) if not wcs: cdMatrix = np.array([1.0, 0.0, 0.0, 1.0]) cdMatrix.shape = (2, 2) wcs = afwGeom.makeSkyWcs(crpix=geom.PointD(0, 0), crval=geom.SpherePoint( 0.0, 0.0, geom.degrees), cdMatrix=cdMatrix) naxis1, naxis2 = md.getScalar("NAXIS1"), md.getScalar("NAXIS2") # Find how many rows there are in the catalogue md = readMetadata(cat) field.addExt(wcs, naxis1, naxis2, md.getScalar("NAXIS2")) if saveWcs: wcss.append((wcs, naxis1, naxis2)) field.finalize() fields.append(field) fields[0].getNext() # number of extensions prefs.getPsfStep() sets = psfexLib.vectorSet() for set in load_samplesLsst(prefs, context, plot=plot): sets.append(set) psfexLib.makeit(fields, sets) ret = [[f.getPsfs() for f in fields], sets] if saveWcs: ret.append(wcssList) return ret
def focalMmToAmpPixel(self, focalPlaneX, focalPlaneY): r"""Given a focal plane position plane return the amplifier position Parameters ---------- focalPlaneX : `float` The x-focal plane position (mm) relative to the centre of the camera focalPlaneY : `float` The y-focal plane position (mm) relative to the centre of the camera N.b. all pixel coordinates have the centre of the bottom-left pixel at (0.0, 0.0) and CCD columns are taken to be vertical, with "R00" in the bottom left of the image Returns ------- detectorName : `str` The name of the detector channel: `int` Channel number of amplifier (1-indexed; identical to HDU) ampX : `int` The column coordinate relative to the corner of the single-amp image ampY : `int` The row coordinate relative to the corner of the single-amp image Raises ------ RuntimeError If the requested position doesn't lie on a detector """ detector, ccdXY = focalMmToCcdPixel(self.camera, geom.PointD(focalPlaneX, focalPlaneY)) amp, ampXY = ccdPixelToAmpPixel(ccdXY, detector) ampX, ampY = ampXY return detector.getName(), ampToChannel(amp), ampX, ampY
def setUp(self): config = SingleFrameMeasurementTask.ConfigClass() config.slots.apFlux = 'base_CircularApertureFlux_12_0' self.schema = afwTable.SourceTable.makeMinimalSchema() self.measureSources = SingleFrameMeasurementTask(self.schema, config=config) width, height = 110, 301 self.mi = afwImage.MaskedImageF(geom.ExtentI(width, height)) self.mi.set(0) sd = 3 # standard deviation of image self.mi.getVariance().set(sd * sd) self.mi.getMask().addMaskPlane("DETECTED") self.ksize = 31 # size of desired kernel sigma1 = 1.75 sigma2 = 2 * sigma1 self.exposure = afwImage.makeExposure(self.mi) self.exposure.setPsf( measAlg.DoubleGaussianPsf(self.ksize, self.ksize, 1.5 * sigma1, 1, 0.1)) cdMatrix = np.array([1.0, 0.0, 0.0, 1.0]) cdMatrix.shape = (2, 2) wcs = afwGeom.makeSkyWcs(crpix=geom.PointD(0, 0), crval=geom.SpherePoint( 0.0, 0.0, geom.degrees), cdMatrix=cdMatrix) self.exposure.setWcs(wcs) # # Make a kernel with the exactly correct basis functions. # Useful for debugging # basisKernelList = [] for sigma in (sigma1, sigma2): basisKernel = afwMath.AnalyticKernel( self.ksize, self.ksize, afwMath.GaussianFunction2D(sigma, sigma)) basisImage = afwImage.ImageD(basisKernel.getDimensions()) basisKernel.computeImage(basisImage, True) basisImage /= np.sum(basisImage.getArray()) if sigma == sigma1: basisImage0 = basisImage else: basisImage -= basisImage0 basisKernelList.append(afwMath.FixedKernel(basisImage)) order = 1 # 1 => up to linear spFunc = afwMath.PolynomialFunction2D(order) exactKernel = afwMath.LinearCombinationKernel(basisKernelList, spFunc) exactKernel.setSpatialParameters([[1.0, 0, 0], [0.0, 0.5 * 1e-2, 0.2e-2]]) rand = afwMath.Random() # make these tests repeatable by setting seed addNoise = True if addNoise: im = self.mi.getImage() afwMath.randomGaussianImage(im, rand) # N(0, 1) im *= sd # N(0, sd^2) del im xarr, yarr = [], [] for x, y in [ (20, 20), (60, 20), (30, 35), (50, 50), (20, 90), (70, 160), (25, 265), (75, 275), (85, 30), (50, 120), (70, 80), (60, 210), (20, 210), ]: xarr.append(x) yarr.append(y) for x, y in zip(xarr, yarr): dx = rand.uniform() - 0.5 # random (centered) offsets dy = rand.uniform() - 0.5 k = exactKernel.getSpatialFunction(1)( x, y) # functional variation of Kernel ... b = (k * sigma1**2 / ((1 - k) * sigma2**2) ) # ... converted double Gaussian's "b" # flux = 80000 - 20*x - 10*(y/float(height))**2 flux = 80000 * (1 + 0.1 * (rand.uniform() - 0.5)) I0 = flux * (1 + b) / (2 * np.pi * (sigma1**2 + b * sigma2**2)) for iy in range(y - self.ksize // 2, y + self.ksize // 2 + 1): if iy < 0 or iy >= self.mi.getHeight(): continue for ix in range(x - self.ksize // 2, x + self.ksize // 2 + 1): if ix < 0 or ix >= self.mi.getWidth(): continue II = I0 * psfVal(ix, iy, x + dx, y + dy, sigma1, sigma2, b) Isample = rand.poisson(II) if addNoise else II self.mi.image[ix, iy, afwImage.LOCAL] += Isample self.mi.variance[ix, iy, afwImage.LOCAL] += II bbox = geom.BoxI(geom.PointI(0, 0), geom.ExtentI(width, height)) self.cellSet = afwMath.SpatialCellSet(bbox, 100) self.footprintSet = afwDetection.FootprintSet( self.mi, afwDetection.Threshold(100), "DETECTED") self.catalog = self.measure(self.footprintSet, self.exposure) for source in self.catalog: try: cand = measAlg.makePsfCandidate(source, self.exposure) self.cellSet.insertCandidate(cand) except Exception as e: print(e) continue
def plotKernelSpatialModel(kernel, kernelCellSet, showBadCandidates=True, numSample=128, keepPlots=True, maxCoeff=10): """Plot the Kernel spatial model. """ try: import matplotlib.pyplot as plt import matplotlib.colors except ImportError as e: print("Unable to import numpy and matplotlib: %s" % e) return x0 = kernelCellSet.getBBox().getBeginX() y0 = kernelCellSet.getBBox().getBeginY() candPos = list() candFits = list() badPos = list() badFits = list() candAmps = list() badAmps = list() for cell in kernelCellSet.getCellList(): for cand in cell.begin(False): if not showBadCandidates and cand.isBad(): continue candCenter = geom.PointD(cand.getXCenter(), cand.getYCenter()) try: im = cand.getTemplateMaskedImage() except Exception: continue targetFits = badFits if cand.isBad() else candFits targetPos = badPos if cand.isBad() else candPos targetAmps = badAmps if cand.isBad() else candAmps # compare original and spatial kernel coefficients kp0 = np.array(cand.getKernel(diffimLib.KernelCandidateF.ORIG).getKernelParameters()) amp = cand.getCandidateRating() targetFits = badFits if cand.isBad() else candFits targetPos = badPos if cand.isBad() else candPos targetAmps = badAmps if cand.isBad() else candAmps targetFits.append(kp0) targetPos.append(candCenter) targetAmps.append(amp) xGood = np.array([pos.getX() for pos in candPos]) - x0 yGood = np.array([pos.getY() for pos in candPos]) - y0 zGood = np.array(candFits) xBad = np.array([pos.getX() for pos in badPos]) - x0 yBad = np.array([pos.getY() for pos in badPos]) - y0 zBad = np.array(badFits) numBad = len(badPos) xRange = np.linspace(0, kernelCellSet.getBBox().getWidth(), num=numSample) yRange = np.linspace(0, kernelCellSet.getBBox().getHeight(), num=numSample) if maxCoeff: maxCoeff = min(maxCoeff, kernel.getNKernelParameters()) else: maxCoeff = kernel.getNKernelParameters() for k in range(maxCoeff): func = kernel.getSpatialFunction(k) dfGood = zGood[:, k] - np.array([func(pos.getX(), pos.getY()) for pos in candPos]) yMin = dfGood.min() yMax = dfGood.max() if numBad > 0: dfBad = zBad[:, k] - np.array([func(pos.getX(), pos.getY()) for pos in badPos]) # Can really screw up the range... yMin = min([yMin, dfBad.min()]) yMax = max([yMax, dfBad.max()]) yMin -= 0.05*(yMax - yMin) yMax += 0.05*(yMax - yMin) fRange = np.ndarray((len(xRange), len(yRange))) for j, yVal in enumerate(yRange): for i, xVal in enumerate(xRange): fRange[j][i] = func(xVal, yVal) fig = plt.figure(k) fig.clf() try: fig.canvas._tkcanvas._root().lift() # == Tk's raise, but raise is a python reserved word except Exception: # protect against API changes pass fig.suptitle('Kernel component %d' % k) # LL ax = fig.add_axes((0.1, 0.05, 0.35, 0.35)) vmin = fRange.min() # - 0.05*np.fabs(fRange.min()) vmax = fRange.max() # + 0.05*np.fabs(fRange.max()) norm = matplotlib.colors.Normalize(vmin=vmin, vmax=vmax) im = ax.imshow(fRange, aspect='auto', norm=norm, extent=[0, kernelCellSet.getBBox().getWidth() - 1, 0, kernelCellSet.getBBox().getHeight() - 1]) ax.set_title('Spatial polynomial') plt.colorbar(im, orientation='horizontal', ticks=[vmin, vmax]) # UL ax = fig.add_axes((0.1, 0.55, 0.35, 0.35)) ax.plot(-2.5*np.log10(candAmps), zGood[:, k], 'b+') if numBad > 0: ax.plot(-2.5*np.log10(badAmps), zBad[:, k], 'r+') ax.set_title("Basis Coefficients") ax.set_xlabel("Instr mag") ax.set_ylabel("Coeff") # LR ax = fig.add_axes((0.55, 0.05, 0.35, 0.35)) ax.set_autoscale_on(False) ax.set_xbound(lower=0, upper=kernelCellSet.getBBox().getHeight()) ax.set_ybound(lower=yMin, upper=yMax) ax.plot(yGood, dfGood, 'b+') if numBad > 0: ax.plot(yBad, dfBad, 'r+') ax.axhline(0.0) ax.set_title('dCoeff (indiv-spatial) vs. y') # UR ax = fig.add_axes((0.55, 0.55, 0.35, 0.35)) ax.set_autoscale_on(False) ax.set_xbound(lower=0, upper=kernelCellSet.getBBox().getWidth()) ax.set_ybound(lower=yMin, upper=yMax) ax.plot(xGood, dfGood, 'b+') if numBad > 0: ax.plot(xBad, dfBad, 'r+') ax.axhline(0.0) ax.set_title('dCoeff (indiv-spatial) vs. x') fig.show() global keptPlots if keepPlots and not keptPlots: # Keep plots open when done def show(): print("%s: Please close plots when done." % __name__) try: plt.show() except Exception: pass print("Plots closed, exiting...") import atexit atexit.register(show) keptPlots = True
if sensorName in mapperSensors: # update the defocal shift deformationCoeff = splitContent[19] defCoeff = float(deformationCoeff) if abs(defCoeff) > 10: sgn = np.sign(defCoeff) # change +/- 1000 to +/- 1500 newDeformationCoeff = str(defCoeff + sgn * 500.0) #print(newDeformationCoeff) newSplitContent[19] = newDeformationCoeff # update the x,y position detector = camera.get(sensorName) bbox = detector.getBBox() xc_px, yc_px = bbox.centerX, bbox.centerY xc_mm, yc_mm = detector.transform(geom.PointD(xc_px, yc_px), cameraGeom.PIXELS, cameraGeom.FOCAL_PLANE) xc_microns, yc_microns = 1000 * xc_mm, 1000 * yc_mm #xpos, ypos = float(splitContent[1]), float(splitContent[2]) #print(xpos, ypos, '-->', # yp_microns, xp_microns, # 'dx:', xpos - yp_microns, # 'dy:', ypos-xp_microns) # [1] is x-pos, [2] is y-pos if derotate_phosim: # x_phosim <-- x_lsst, i.e. no transpose newSplitContent[1] = str(round(xc_microns, 2)) newSplitContent[2] = str(round(yc_microns, 2))
def main(butler, visits, fields, fieldRadius, showCCDs=False, aitoff=False, alpha=0.2, byFilter=False, byVisit=False, title="", verbose=False): ra, dec = [], [] filters = {} _visits, visits = visits, [] for v in _visits: try: exp = butler.get("raw", visit=v, ccd=49) except RuntimeError as e: if verbose: print(e, file=sys.stderr) continue ccd = exp.getDetector() xy = ccd.getPixelFromPosition(afwCG.FpPoint(0, 0)) sky = exp.getWcs().pixelToSky(xy) visits.append(v) ra.append(sky[0].asDegrees()) dec.append(sky[1].asDegrees()) filters[v] = exp.getFilter().getName() plt.clf() if aitoff: axes = plt.gcf().add_axes((0.1, 0.1, 0.85, 0.80), projection="aitoff") axes.grid(1) else: axes = plt.gca() axes.set_aspect('equal') ctypes = dict( BIAS="orange", DARK="cyan", ) if byFilter: ctypeFilters = dict( g="green", r="red", r1="orange", i="magenta", z="brown", y="black", nb0921='darkgray', ) else: ctypeKeys = {} colors = list("rgbcmyk") + ["orange", "brown", "orchid" ] # colours for ctypeKeys if aitoff: fieldRadius = np.radians(fieldRadius) dec = np.radians(dec) ra = np.array(ra) ra = np.radians(np.where(ra > 180, ra - 360, ra)) plots, labels = [], [] for v, r, d in zip(visits, ra, dec): field = fields.get(v) if verbose: print("Drawing %s %s \r" % (v, field), end=' ', flush=True) if byFilter: facecolor = ctypes.get(field, ctypeFilters.get(filters[v], "gray")) else: key = v if byVisit else field if key not in ctypeKeys: ctypeKeys[key] = colors[len(ctypeKeys) % len(colors)] facecolor = ctypeKeys[key] circ = Circle(xy=(r, d), radius=fieldRadius, fill=False if showCCDs else True, facecolor=facecolor, alpha=alpha) axes.add_artist(circ) if showCCDs: pathCodes = [ Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY, ] for ccd in butler.queryMetadata("raw", "visit", ["ccd"], visit=v): try: md = butler.get("raw_md", visit=v, ccd=ccd) except RuntimeError as e: if verbose: print(e, file=sys.stderr) continue width, height = md.getScalar("NAXIS1"), md.getScalar("NAXIS2") wcs = afwGeom.makeSkyWcs(md) verts = [] for p in [(0, 0), (width, 0), (width, height), (0, height)]: sky = wcs.pixelToSky(geom.PointD(*p)) verts.append([sky[0].asDegrees(), sky[1].asDegrees()]) verts.append((0, 0)) # dummy axes.add_patch( PathPatch(Path(verts, pathCodes), alpha=alpha, facecolor=facecolor)) if byFilter: key = filters[v] else: key = v if byVisit else field if not labels.count(key): plots.append(Circle((0, 0), facecolor=facecolor)) labels.append(key) plt.legend(plots, labels, loc='best', bbox_to_anchor=(0, 1.02, 1, 0.102)).draggable() if not aitoff: raRange = np.max(ra) - np.min(ra) + 1.2 * fieldRadius decRange = np.max(dec) - np.min(dec) + 1.2 * fieldRadius raRange *= np.cos(np.radians(np.mean(dec))) plt.xlim(0.5 * (np.max(ra) + np.min(ra)) + raRange * np.array((1, -1))) plt.ylim(0.5 * (np.max(dec) + np.min(dec)) + decRange * np.array((-1, 1))) plt.xlabel("ra") plt.ylabel("dec") return plt
def main(dataDir, visit, title="", outputTxtFileName=None, showFwhm=False, minFwhm=None, maxFwhm=None, correctDistortion=False, showEllipticity=False, ellipticityDirection=False, showNdataFwhm=False, showNdataEll=False, minNdata=None, maxNdata=None, gridPoints=30, verbose=False): butler = dafPersist.ButlerFactory(mapper=hscSim.HscSimMapper(root=dataDir)).create() camera = butler.get("camera") if not (showFwhm or showEllipticity or showNdataFwhm or showNdataEll or outputTxtFileName): showFwhm = True # # Get a dict of cameraGeom::Ccd indexed by serial number # ccds = {} for raft in camera: for ccd in raft: ccd.setTrimmed(True) ccds[ccd.getId().getSerial()] = ccd # # Read all the tableSeeingMap files, converting their (x, y) to focal # plane coordinates # xArr = [] yArr = [] ellArr = [] fwhmArr = [] paArr = [] aArr = [] bArr = [] e1Arr = [] e2Arr = [] elle1e2Arr = [] for tab in butler.subset("tableSeeingMap", visit=visit): # we could use tab.datasetExists() but it prints a rude message fileName = butler.get("tableSeeingMap_filename", **tab.dataId)[0] if not os.path.exists(fileName): continue with open(fileName) as fd: ccd = None for line in fd.readlines(): if re.search(r"^\s*#", line): continue fields = [float(_) for _ in line.split()] if ccd is None: ccd = ccds[int(fields[0])] x, y, fwhm, ell, pa, a, b = fields[1:8] x, y = ccd.getPositionFromPixel(geom.PointD(x, y)).getMm() xArr.append(x) yArr.append(y) ellArr.append(ell) fwhmArr.append(fwhm) paArr.append(pa) aArr.append(a) bArr.append(b) if len(fields) == 11: e1 = fields[8] e2 = fields[9] elle1e2 = fields[10] else: e1 = -9999. e2 = -9999. elle1e2 = -9999. e1Arr.append(e1) e2Arr.append(e2) elle1e2Arr.append(elle1e2) xArr = np.array(xArr) yArr = np.array(yArr) ellArr = np.array(ellArr) fwhmArr = np.array(fwhmArr)*0.168 # arcseconds paArr = np.radians(np.array(paArr)) aArr = np.array(aArr) bArr = np.array(bArr) e1Arr = np.array(e1Arr) e2Arr = np.array(e2Arr) elle1e2Arr = np.array(elle1e2Arr) if correctDistortion: import lsst.afw.geom.ellipses as afwEllipses dist = camera.getDistortion() for i in range(len(aArr)): axes = afwEllipses.Axes(aArr[i], bArr[i], paArr[i]) if False: # testing only! axes = afwEllipses.Axes(1.0, 1.0, np.arctan2(yArr[i], xArr[i])) quad = afwGeom.Quadrupole(axes) quad = quad.transform(dist.computeQuadrupoleTransform(geom.PointD(xArr[i], yArr[i]), False)) axes = afwEllipses.Axes(quad) aArr[i], bArr[i], paArr[i] = axes.getA(), axes.getB(), axes.getTheta() ellArr = 1 - bArr/aArr if len(xArr) == 0: gridPoints = 0 xs, ys = [], [] else: N = gridPoints*1j extent = [min(xArr), max(xArr), min(yArr), max(yArr)] xs, ys = np.mgrid[extent[0]:extent[1]:N, extent[2]:extent[3]:N] title = [title, ] title.append("\n#") if outputTxtFileName: f = open(outputTxtFileName, 'w') f.write("# %s visit %s\n" % (" ".join(title), visit)) for x, y, ell, fwhm, pa, a, b, e1, e2, elle1e2 \ in zip(xArr, yArr, ellArr, fwhmArr, paArr, aArr, bArr, e1Arr, e2Arr, elle1e2Arr): f.write('%f %f %f %f %f %f %f %f %f %f\n' % (x, y, ell, fwhm, pa, a, b, e1, e2, elle1e2)) if showFwhm: title.append("FWHM (arcsec)") if len(xs) > 0: fwhmResampled = griddata(xArr, yArr, fwhmArr, xs, ys) plt.imshow(fwhmResampled.T, extent=extent, vmin=minFwhm, vmax=maxFwhm, origin='lower') plt.colorbar() if outputTxtFileName: ndataGrids = getNumDataGrids(xArr, yArr, fwhmArr, xs, ys) f = open(outputTxtFileName+'-fwhm-grid.txt', 'w') f.write("# %s visit %s\n" % (" ".join(title), visit)) for xline, yline, fwhmline, ndataline \ in zip(xs.tolist(), ys.tolist(), fwhmResampled.tolist(), ndataGrids): for xx, yy, fwhm, ndata in zip(xline, yline, fwhmline, ndataline): if fwhm is None: fwhm = -9999 f.write('%f %f %f %d\n' % (xx, yy, fwhm, ndata)) elif showEllipticity: title.append("Ellipticity") scale = 4 if ellipticityDirection: # we don't care about the magnitude ellArr = 0.1 u = -ellArr*np.cos(paArr) v = -ellArr*np.sin(paArr) if gridPoints > 0: u = griddata(xArr, yArr, u, xs, ys) v = griddata(xArr, yArr, v, xs, ys) x, y = xs, ys else: x, y = xArr, yArr Q = plt.quiver(x, y, u, v, scale=scale, pivot="middle", headwidth=0, headlength=0, headaxislength=0, ) keyLen = 0.10 if not ellipticityDirection: # we care about the magnitude plt.quiverkey(Q, 0.20, 0.95, keyLen, "e=%g" % keyLen, labelpos='W') if outputTxtFileName: ndataGrids = getNumDataGrids(xArr, yArr, ellArr, xs, ys) f = open(outputTxtFileName+'-ell-grid.txt', 'w') f.write("# %s visit %s\n" % (" ".join(title), visit)) # f.write('# %f %f %f %f %f %f %f\n' % (x, y, ell, fwhm, pa, a, b)) for xline, yline, uline, vline, ndataline \ in zip(x.tolist(), y.tolist(), u.tolist(), v.tolist(), ndataGrids): for xx, yy, uu, vv, ndata in zip(xline, yline, uline, vline, ndataline): if uu is None: uu = -9999 if vv is None: vv = -9999 f.write('%f %f %f %f %d\n' % (xx, yy, uu, vv, ndata)) elif showNdataFwhm: title.append("N per fwhm grid") if len(xs) > 0: ndataGrids = getNumDataGrids(xArr, yArr, fwhmArr, xs, ys) plt.imshow(ndataGrids, interpolation='nearest', extent=extent, vmin=minNdata, vmax=maxNdata, origin='lower') plt.colorbar() else: pass elif showNdataEll: title.append("N per ell grid") if len(xs) > 0: ndataGrids = getNumDataGrids(xArr, yArr, ellArr, xs, ys) plt.imshow(ndataGrids, interpolation='nearest', extent=extent, vmin=minNdata, vmax=maxNdata, origin='lower') plt.colorbar() else: pass # plt.plot(xArr, yArr, "r.") # plt.plot(xs, ys, "b.") plt.axes().set_aspect('equal') plt.axis([-20000, 20000, -20000, 20000]) def frameInfoFrom(filepath): with fits.open(filepath) as hdul: h = hdul[0].header # 'object=ABELL2163 filter=HSC-I exptime=360.0 alt=62.11143274 ' # ' azm=202.32265181 hst=(23:40:08.363-23:40:48.546)' return 'object=%s filter=%s exptime=%.1f azm=%.2f hst=%s' % \ (h['OBJECT'], h['FILTER01'], h['EXPTIME'], h['AZIMUTH'], h['HST']) title.insert(0, frameInfoFrom(butler.get('raw_filename', {'visit': visit, 'ccd': 0})[0])) title.append(r'$\langle$FWHM$\rangle %4.2f$"' % np.median(fwhmArr)) plt.title("%s visit=%s" % (" ".join(title), visit), fontsize=9) return plt
def showPsf(psf, set, ext=None, wcsData=None, trim=0, nspot=5, diagnostics=False, outDir="", frame=None, title=None): """Show a PSF on display (e.g., ds9) """ if ext is not None: psf = psf[ext] if wcsData: if ext is not None: wcsData = wcsData[ext] wcs, naxis1, naxis2 = wcsData else: wcs, naxis1, naxis2 = None, None, None naxis = [naxis1, naxis2] for i in range(2): if naxis[i] is None: # cmin, cmax are the range of input star positions cmin, cmax = [ set.getContextOffset(i) + d * set.getContextScale(i) for d in (-0.5, 0.5) ] naxis[i] = cmax + cmin # a decent guess if naxis[0] > naxis[1]: nx, ny = int(nspot * naxis[0] / float(naxis[1]) + 0.5), nspot else: nx, ny = nspot, int(nspot * naxis[1] / float(naxis[0]) + 0.5) mos = afwDisplay.utils.Mosaic(gutter=2, background=0.02) xpos, ypos = np.linspace(0, naxis[0], nx), np.linspace(0, naxis[1], ny) for y in ypos: for x in xpos: psf.build(x, y) im = afwImage.ImageF(*psf.getLoc().shape) im.getArray()[:] = psf.getLoc() im /= float(im.getArray().max()) if trim: if trim > im.getHeight() // 2: trim = im.getHeight() // 2 im = im[trim:-trim, trim:-trim] mos.append(im) mosaic = mos.makeMosaic(mode=nx) if frame is not None: afwDisplay.Display(frame=frame).mtv(mosaic, title=title) # # Figure out the WCS for the mosaic # pos = [] pos.append([geom.PointD(0, 0), wcs.pixelToSky(geom.PointD(0, 0))]) pos.append([ geom.PointD(*mosaic.getDimensions()), wcs.pixelToSky(geom.PointD(naxis1, naxis2)) ]) CD = [] for i in range(2): delta = pos[1][1][i].asDegrees() - pos[0][1][i].asDegrees() CD.append(delta / (pos[1][0][i] - pos[0][0][i])) CD = np.array(CD) CD.shape = (2, 2) mosWcs = afwGeom.makeSkyWcs(crval=pos[0][0], crpix=pos[0][1], cdMatrix=CD) if ext is not None: title = "%s-%d" % (title, ext) if frame is not None: afwDisplay.Display(frame=frame).mtv(mosaic, title=title, wcs=mosWcs) if diagnostics: outFile = "%s-mod.fits" % title if outDir: outFile = os.path.join(outDir, outFile) mosaic.writeFits(outFile, mosWcs.getFitsMetadata()) mos = afwDisplay.utils.Mosaic(gutter=4, background=0.002) for i in range(set.getNsample()): s = set.getSample(i) if ext is not None and s.getExtindex() != ext: continue smos = afwDisplay.utils.Mosaic(gutter=2, background=-0.003) for func in [s.getVig, s.getVigResi]: arr = func() if func == s.getVig: norm = float(arr.max()) if True else s.getNorm() arr /= norm im = afwImage.ImageF(*arr.shape) im.getArray()[:] = arr smos.append(im) mos.append(smos.makeMosaic(mode="x")) mosaic = mos.makeMosaic(title=title) if frame is not None: afwDisplay.Display(frame=frame + 1).mtv(mosaic, title=title) if diagnostics: outFile = "%s-psfstars.fits" % title if outDir: outFile = os.path.join(outDir, outFile) mosaic.writeFits(outFile)
def main(update_euler, use_obs_euler): ''' A program to perform updates to the content of focalplanelayout.txt and segmentation.txt # read all the lines to a list. # as a base for all changes is the copy of segmentation.txt from before I started # to change anything that had to do with orientation, # i.e. as of v1.0.4 https://github.com/lsst-ts/phosim_syseng4/releases/tag/v1.0.4 # The geometry update for LSSTCam was https://github.com/lsst-ts/phosim_syseng4/releases/tag/v1.0.5 # The geometry update for LSSTComCam was https://github.com/lsst-ts/phosim_syseng4/releases/tag/v1.0.6 # Fix gain, variance for LSSTComCam https://github.com/lsst-ts/phosim_syseng4/releases/tag/v1.0.7 ########################################### # # # Update focalplanelayout for lsstCam # # # ########################################### ''' camera = LsstCam().getCamera() # Need to store each line of the other focalplanelayout to convey the zernike perturbations ... headerLines, contentLinesZer = up.readFile( '/project/scichris/aos/phosim_syseng4/data/lsst/focalplanelayout_old.txt' ) splitLinesZerDic = {} for line in contentLinesZer: splitLinesZerDic[line.split() [0]] = line.split() # sensor name is the key # read the file and split into two lists headerLines, contentLines = up.readFile( '/project/scichris/aos/phosim_syseng4/data/lsst/focalplanelayout_old.txt' ) # UPDATE EULER ANGLE? #update_euler = True # RESHUFFLE OFF-DIAGONAL SENSORS reshuffle_sensors = False if update_euler: print('Updating Euler angle ') ticket_number = 'DM-30367' # both: obs_lsst orientation else: if use_obs_euler: print('Using obs_lsst Euler angle') ticket_number = 'DM-30367' else: print('Keeping original Euler angle') ticket_number = 'DM-28557' # lsstCam if reshuffle_sensors: print('Reshuffle sensors') ticket_number = 'DM-30367' # update the content newContentLines = [] for line in contentLines: splitContent = line.split() newSplitContent = splitContent.copy() # update the sensor name sensorName = splitContent[0] newName = up.getNewSensorName(sensorName) newSplitContent[0] = newName # update the defocal shift deformationCoeff = splitContent[19] defCoeff = float(deformationCoeff) if abs(defCoeff) > 10: sgn = np.sign(defCoeff) # change +/- 1000 to +/- 1500 newDeformationCoeff = str(defCoeff + sgn * 500.0) #print(newDeformationCoeff) newSplitContent[19] = newDeformationCoeff # update the x,y position detector = camera.get(newName) bbox = detector.getBBox() xc_px, yc_px = bbox.centerX, bbox.centerY xc_mm, yc_mm = detector.transform(geom.PointD(xc_px, yc_px), cameraGeom.PIXELS, cameraGeom.FOCAL_PLANE) xc_microns, yc_microns = 1000 * xc_mm, 1000 * yc_mm xpos, ypos = float(splitContent[1]), float(splitContent[2]) #print(xpos, ypos, '-->', # yp_microns, xp_microns, # 'dx:', xpos - yp_microns, # 'dy:', ypos-xp_microns) newSplitContent[1] = str(round(yc_microns, 2)) newSplitContent[2] = str(round(xc_microns, 2)) # update the number of x px, y px xpx, ypx = splitContent[4], splitContent[5] xnew, ynew = bbox.getHeight(), bbox.getWidth( ) # rotated wrt lsstCam ... newSplitContent[4] = str(xnew) newSplitContent[5] = str(ynew) # Update Euler angles for corner sensors only if update_euler: corner_rotations = { 'R04_SW0': 180, 'R40_SW0': 180, 'R44_SW0': 180, 'R00_SW0': 180 } if newName in corner_rotations.keys(): eulerAngle1 = float(splitContent[12]) rotation = corner_rotations[newName] newEulerAngle1 = eulerAngle1 + rotation print( f'For {newName} changed from {eulerAngle1} to {newEulerAngle1}' ) newSplitContent[12] = str(newEulerAngle1) if use_obs_euler: yaw = -detector.getOrientation().getYaw().asDegrees() newSplitContent[12] = str(yaw) # update the dx, dy - since we know the actual position, set these to 0 ... dx, dy, dz = float(splitContent[15]), float(splitContent[16]), float( splitContent[17]) newSplitContent[15] = '0' # <-- set dx to 0 newSplitContent[16] = '0' # <-- set dy to 0 # change sensor.txt to zern, and append all parts that follow... # that way I can also use the 'new' format of focalplanelayout, # changing all after 'sensor.txt' to zern and the coefficients ... newSplitContent[18] = 'zern' newSplitContent[20:] = splitLinesZerDic[sensorName][20:] if reshuffle_sensors: raftName, sensorName = newName.split('_') sensorNameMap = { 'S01': 'S10', 'S02': 'S20', 'S12': 'S21', 'S21': 'S12', 'S10': 'S01', 'S20': 'S02' } if sensorName in sensorNameMap.keys(): newSensorName = sensorNameMap[sensorName] updatedName = f'{raftName}_{newSensorName}' newSplitContent[0] = updatedName print('\n Changed ', newName, ' to ', updatedName) # append updated line newContentLines.append(' '.join(newSplitContent) + '\n') # store the updated version: the header and updated content lines fname = f"/project/scichris/aos/phosim_syseng4/data/lsst/focalplanelayout_{ticket_number}.txt" f = open(fname, "w") f.writelines(headerLines) f.writelines(newContentLines) f.close() print('Saved as %s' % fname)