def norm_dist(src1, src2): """ Calculate the normalised distance between two sources. Sources are elliptical Gaussians. The normalised distance is calculated as the GCD distance between the centers, divided by quadrature sum of the radius of each ellipse along a line joining the two ellipses. For ellipses that touch at a single point, the normalized distance will be 1/sqrt(2). Parameters ---------- src1, src2 : object The two positions to compare. Objects must have the following parameters: (ra, dec, a, b, pa). Returns ------- dist: float The normalised distance. """ if src1 == src2: return 0 dist = gcd(src1.ra, src1.dec, src2.ra, src2.dec) # degrees # the angle between the ellipse centers phi = bear(src1.ra, src1.dec, src2.ra, src2.dec) # Degrees # Calculate the radius of each ellipse along a line that joins their centers. r1 = src1.a * src1.b / np.hypot(src1.a * np.sin(np.radians(phi - src1.pa)), src1.b * np.cos(np.radians(phi - src1.pa))) r2 = src2.a * src2.b / np.hypot( src2.a * np.sin(np.radians(180 + phi - src2.pa)), src2.b * np.cos(np.radians(180 + phi - src2.pa))) R = dist / (np.hypot(r1, r2) / 3600) return R
def pix2sky_vec(self, pixel, r, theta): """ Convert a vector from pixel to sky coords vector is calculated at an origin pixel=(x,y) and has a magnitude (r) [in pixels] and an angle (theta) [in degrees] input: pixel - (x,y) of origin r - magnitude in pixels theta - in degrees return: ra,dec - corresponding to pixels x,y r,pa - magnitude and angle (degrees) of the original vector, as measured on the sky """ ra1, dec1 = self.pix2sky(pixel) x, y = pixel a = [ x + r * np.cos(np.radians(theta)), y + r * np.sin(np.radians(theta)) ] locations = self.pix2sky(a) ra2, dec2 = locations a = gcd(ra1, dec1, ra2, dec2) pa = bear(ra1, dec1, ra2, dec2) return ra1, dec1, a, pa
def pix2sky_ellipse(self, pixel, sx, sy, theta): """ Convert an ellipse from pixel to sky coords sx/sy vectors are calculated at an origin pos=(x,y) Input parameters are: x,y - the x,y pixels corresponding to the ra/dec position sx, sy - the major minor axes (FWHM) in pixels theta - the position angle in degrees Output params are all in degrees :param pixel: [x,y] of the ellipse center :param sx: major axis :param sy: minor axis :param theta: position angle :return: ra, dec, a, b, pa """ ra, dec = self.pix2sky(pixel) x, y = pixel v_sx = [ x + sx * np.cos(np.radians(theta)), y + sx * np.sin(np.radians(theta)) ] ra2, dec2 = self.pix2sky(v_sx) major = gcd(ra, dec, ra2, dec2) pa = bear(ra, dec, ra2, dec2) v_sy = [ x + sy * np.cos(np.radians(theta - 90)), y + sy * np.sin(np.radians(theta - 90)) ] ra2, dec2 = self.pix2sky(v_sy) minor = gcd(ra, dec, ra2, dec2) pa2 = bear(ra, dec, ra2, dec2) - 90 # The a/b vectors are perpendicular in sky space, but not always in pixel space # so we have to account for this by calculating the angle between the two vectors # and modifying the minor axis length defect = pa - pa2 minor *= abs(np.cos(np.radians(defect))) return ra, dec, major, minor, pa
def pix2sky_ellipse(self, pixel, sx, sy, theta): """ Convert an ellipse from pixel to sky coords sx/sy vectors are calculated at an origin pos=(x,y) Input parameters are: x,y - the x,y pixels corresponding to the ra/dec position sx, sy - the major minor axes (FWHM) in pixels theta - the position angle in degrees Output params are all in degrees :param pixel: [x,y] of the ellipse center :param sx: major axis :param sy: minor axis :param theta: position angle :return: ra, dec, a, b, pa """ ra, dec = self.pix2sky(pixel) x, y = pixel v_sx = [x + sx * np.cos(np.radians(theta)), y + sx * np.sin(np.radians(theta))] ra2, dec2 = self.pix2sky(v_sx) major = gcd(ra, dec, ra2, dec2) pa = bear(ra, dec, ra2, dec2) v_sy = [x + sy * np.cos(np.radians(theta-90)), y + sy * np.sin(np.radians(theta-90))] ra2, dec2 = self.pix2sky(v_sy) minor = gcd(ra, dec, ra2, dec2) pa2 = bear(ra, dec, ra2, dec2) - 90 # The a/b vectors are perpendicular in sky space, but not always in pixel space # so we have to account for this by calculating the angle between the two vectors # and modifying the minor axis length defect = pa - pa2 minor *= abs(np.cos(np.radians(defect))) return ra, dec, major, minor, pa
def norm_dist(src1,src2): """ Calculate the normalised distance between two sources. Sources are elliptical gaussians. :param src1: :param src2: :return: """ if src1 == src2: return 0 dist = gcd(src1.ra, src1.dec, src2.ra, src2.dec) # degrees # the angle between the ellipse centers phi = bear(src1.ra, src1.dec, src2.ra, src2.dec) # Degrees # Calculate the radius of each ellpise along a line that joins their centers. r1 = src1.a*src1.b / np.hypot(src1.a * np.sin(np.radians(phi - src1.pa)), src1.b * np.cos(np.radians(phi - src1.pa))) r2 = src2.a*src2.b / np.hypot(src2.a * np.sin(np.radians(180 + phi - src2.pa)), src2.b * np.cos(np.radians(180 + phi - src2.pa))) R = dist / (np.hypot(r1,r2) / 3600) return R
def norm_dist(src1, src2): """ Calculate the normalised distance between two sources. Sources are elliptical gaussians. :param src1: :param src2: :return: """ if src1 == src2: return 0 dist = gcd(src1.ra, src1.dec, src2.ra, src2.dec) # degrees # the angle between the ellipse centers phi = bear(src1.ra, src1.dec, src2.ra, src2.dec) # Degrees # Calculate the radius of each ellpise along a line that joins their centers. r1 = src1.a * src1.b / np.hypot(src1.a * np.sin(np.radians(phi - src1.pa)), src1.b * np.cos(np.radians(phi - src1.pa))) r2 = src2.a * src2.b / np.hypot( src2.a * np.sin(np.radians(180 + phi - src2.pa)), src2.b * np.cos(np.radians(180 + phi - src2.pa))) R = dist / (np.hypot(r1, r2) / 3600) return R
def pix2sky_vec(self, pixel, r, theta): """ Convert a vector from pixel to sky coords vector is calculated at an origin pixel=(x,y) and has a magnitude (r) [in pixels] and an angle (theta) [in degrees] input: pixel - (x,y) of origin r - magnitude in pixels theta - in degrees return: ra,dec - corresponding to pixels x,y r,pa - magnitude and angle (degrees) of the original vector, as measured on the sky """ ra1, dec1 = self.pix2sky(pixel) x, y = pixel a = [x + r * np.cos(np.radians(theta)), y + r * np.sin(np.radians(theta))] locations = self.pix2sky(a) ra2, dec2 = locations a = gcd(ra1, dec1, ra2, dec2) pa = bear(ra1, dec1, ra2, dec2) return ra1, dec1, a, pa
def errors(source, model, wcshelper): """ Convert pixel based errors into sky coord errors :param source: Source object :param wcshelper: WCSHelper object :return: """ # if the source wasn't fit then all errors are -1 if source.flags & (flags.NOTFIT | flags.FITERR): source.err_peak_flux = source.err_a = source.err_b = source.err_pa = -1 source.err_ra = source.err_dec = source.err_int_flux = -1 return source # copy the errors from the model prefix = "c{0}_".format(source.source) err_amp = model[prefix+'amp'].stderr xo,yo = model[prefix+'xo'].value, model[prefix+'yo'].value err_xo = model[prefix+'xo'].stderr err_yo = model[prefix+'yo'].stderr sx, sy = model[prefix+'sx'].value, model[prefix+'sy'].value err_sx = model[prefix+'sx'].stderr err_sy = model[prefix+'sy'].stderr theta = model[prefix+'theta'].value err_theta = model[prefix+'theta'].stderr source.err_peak_flux = err_amp pix_errs = [err_xo,err_yo,err_sx,err_sy,err_theta] # check for inf/nan errors -> these sources have poor fits. if not all([ a is not None and np.isfinite(a) for a in pix_errs]): source.flags |= flags.FITERR source.err_peak_flux = source.err_a = source.err_b = source.err_pa = -1 source.err_ra = source.err_dec = source.err_int_flux = -1 return source # position errors if model[prefix + 'xo'].vary and model[prefix + 'yo'].vary: ref = wcshelper.pix2sky([xo,yo]) offset = wcshelper.pix2sky([xo+err_xo,yo+err_yo]) source.err_ra = gcd(ref[0], ref[1], offset[0], ref[1]) source.err_dec = gcd(ref[0], ref[1], ref[0], offset[1]) else: source.err_ra = source.err_dec = -1 if model[prefix + 'sx'].vary and model[prefix + 'sy'].vary: # major axis error ref = wcshelper.pix2sky([xo+sx*np.cos(np.radians(theta)),yo+sy*np.sin(np.radians(theta))]) offset = wcshelper.pix2sky([xo+(sx+err_sx)*np.cos(np.radians(theta)),yo+sy*np.sin(np.radians(theta))]) source.err_a = gcd(ref[0],ref[1],offset[0],offset[1]) * 3600 # minor axis error ref = wcshelper.pix2sky([xo+sx*np.cos(np.radians(theta+90)),yo+sy*np.sin(np.radians(theta+90))]) offset = wcshelper.pix2sky([xo+sx*np.cos(np.radians(theta+90)),yo+(sy+err_sy)*np.sin(np.radians(theta+90))]) source.err_b = gcd(ref[0], ref[1], offset[0], offset[1]) * 3600 else: source.err_a = source.err_b = -1 if model[prefix+'theta'].vary: # pa error ref = wcshelper.pix2sky([xo,yo]) off1 = wcshelper.pix2sky([xo+sx*np.cos(np.radians(theta)),yo+sy*np.sin(np.radians(theta))]) off2 = wcshelper.pix2sky([xo+sx*np.cos(np.radians(theta+err_theta)),yo+sy*np.sin(np.radians(theta+err_theta))]) source.err_pa = abs(bear(ref[0], ref[1], off1[0], off1[1]) - bear(ref[0], ref[1], off2[0], off2[1])) else: source.err_pa = -1 sqerr = 0 sqerr += (source.err_peak_flux/source.peak_flux)**2 if source.err_peak_flux >0 else 0 sqerr += (source.err_a/source.a)**2 if source.err_a > 0 else 0 sqerr += (source.err_b/source.b)**2 if source.err_b > 0 else 0 source.err_int_flux = source.int_flux*np.sqrt(sqerr) # logging.info("src ({0},{1})".format(source.island,source.source)) # logging.info(" pixel errs {0}".format([err_xo, err_yo, err_sx, err_sy, err_theta])) # logging.info(" sky errs {0}".format([source.err_ra, source.err_dec, source.err_a, source.err_b, source.err_pa])) return source
def errors(source, model, wcshelper): """ Convert pixel based errors into sky coord errors :param source: Source object :param wcshelper: WCSHelper object :return: """ # if the source wasn't fit then all errors are -1 if source.flags & (flags.NOTFIT | flags.FITERR): source.err_peak_flux = source.err_a = source.err_b = source.err_pa = -1 source.err_ra = source.err_dec = source.err_int_flux = -1 return source # copy the errors from the model prefix = "c{0}_".format(source.source) err_amp = model[prefix + 'amp'].stderr xo, yo = model[prefix + 'xo'].value, model[prefix + 'yo'].value err_xo = model[prefix + 'xo'].stderr err_yo = model[prefix + 'yo'].stderr sx, sy = model[prefix + 'sx'].value, model[prefix + 'sy'].value err_sx = model[prefix + 'sx'].stderr err_sy = model[prefix + 'sy'].stderr theta = model[prefix + 'theta'].value err_theta = model[prefix + 'theta'].stderr source.err_peak_flux = err_amp pix_errs = [err_xo, err_yo, err_sx, err_sy, err_theta] # check for inf/nan errors -> these sources have poor fits. if not all([a is not None and np.isfinite(a) for a in pix_errs]): source.flags |= flags.FITERR source.err_peak_flux = source.err_a = source.err_b = source.err_pa = -1 source.err_ra = source.err_dec = source.err_int_flux = -1 return source # position errors if model[prefix + 'xo'].vary and model[prefix + 'yo'].vary: ref = wcshelper.pix2sky([xo, yo]) offset = wcshelper.pix2sky([xo + err_xo, yo + err_yo]) source.err_ra = gcd(ref[0], ref[1], offset[0], ref[1]) source.err_dec = gcd(ref[0], ref[1], ref[0], offset[1]) else: source.err_ra = source.err_dec = -1 if model[prefix + 'sx'].vary and model[prefix + 'sy'].vary: # major axis error ref = wcshelper.pix2sky([ xo + sx * np.cos(np.radians(theta)), yo + sy * np.sin(np.radians(theta)) ]) offset = wcshelper.pix2sky([ xo + (sx + err_sx) * np.cos(np.radians(theta)), yo + sy * np.sin(np.radians(theta)) ]) source.err_a = gcd(ref[0], ref[1], offset[0], offset[1]) * 3600 # minor axis error ref = wcshelper.pix2sky([ xo + sx * np.cos(np.radians(theta + 90)), yo + sy * np.sin(np.radians(theta + 90)) ]) offset = wcshelper.pix2sky([ xo + sx * np.cos(np.radians(theta + 90)), yo + (sy + err_sy) * np.sin(np.radians(theta + 90)) ]) source.err_b = gcd(ref[0], ref[1], offset[0], offset[1]) * 3600 else: source.err_a = source.err_b = -1 if model[prefix + 'theta'].vary: # pa error ref = wcshelper.pix2sky([xo, yo]) off1 = wcshelper.pix2sky([ xo + sx * np.cos(np.radians(theta)), yo + sy * np.sin(np.radians(theta)) ]) off2 = wcshelper.pix2sky([ xo + sx * np.cos(np.radians(theta + err_theta)), yo + sy * np.sin(np.radians(theta + err_theta)) ]) source.err_pa = abs( bear(ref[0], ref[1], off1[0], off1[1]) - bear(ref[0], ref[1], off2[0], off2[1])) else: source.err_pa = -1 sqerr = 0 sqerr += (source.err_peak_flux / source.peak_flux)**2 if source.err_peak_flux > 0 else 0 sqerr += (source.err_a / source.a)**2 if source.err_a > 0 else 0 sqerr += (source.err_b / source.b)**2 if source.err_b > 0 else 0 source.err_int_flux = source.int_flux * np.sqrt(sqerr) # logging.info("src ({0},{1})".format(source.island,source.source)) # logging.info(" pixel errs {0}".format([err_xo, err_yo, err_sx, err_sy, err_theta])) # logging.info(" sky errs {0}".format([source.err_ra, source.err_dec, source.err_a, source.err_b, source.err_pa])) return source