def commonbeam(major1, minor1, pa1, major2, minor2, pa2): """ Create a smallest bounding ellipse around two other ellipses. Give ellipse dimensions as astropy units quantities. """ major1 = ucheck(major1, unit=u.deg) minor1 = ucheck(minor1, unit=u.deg) pa1 = ucheck(pa1, unit=u.deg) major2 = ucheck(major2, unit=u.deg) minor2 = ucheck(minor2, unit=u.deg) pa2 = ucheck(pa2, unit=u.deg) somebeams = Beams([major1.to(u.arcsec), major2.to(u.arcsec)]*u.arcsec, [minor1.to(u.arcsec), minor2.to(u.arcsec)]*u.arcsec, [pa1, pa2]*u.deg) for tolerance in (1e-4, 5e-5, 1e-5, 1e-6, 1e-7): try: common = somebeams.common_beam(tolerance=tolerance) break except BeamError: continue new_major = common._major new_minor = common._minor new_pa = common._pa return new_major.to(u.deg), new_minor.to(u.deg), new_pa
def common_beam(self, circbeam=True): """ Return parameters of the smallest common beam Parameters ---------- circbeam: bool, optional. Default True - force beam circular Returns ------- bmaj, bmin, bpa: beam in deg """ if circbeam: maxmaj = np.max([image.get_beam()[0] for image in self.images]) target_beam = [maxmaj * 1.01, maxmaj * 1.01, 0.] # add 1% to prevent crash in convolution else: from radio_beam import Beams my_beams = Beams([image.get_beam()[0] for image in self.images] * u.deg, [image.get_beam()[1] for image in self.images] * u.deg, [image.get_beam()[2] for image in self.images] * u.deg) common_beam = my_beams.common_beam() target_beam = [ common_beam.major.to_value('deg'), common_beam.minor.to_value('deg'), common_beam.pa.to_value('deg') ] return target_beam
def commonbeam(major1, minor1, pa1, major2, minor2, pa2): """ Create a smallest bounding ellipse around two other ellipses. Give ellipse dimensions as astropy units quantities. """ somebeams = Beams([major1.to(u.arcsec), major2.to(u.arcsec)] * u.arcsec, [minor1.to(u.arcsec), minor2.to(u.arcsec)] * u.arcsec, [pa1.to(u.deg), pa2.to(u.deg)] * u.deg) common = somebeams.common_beam() new_major = common._major new_minor = common._minor new_pa = common._pa return new_major.to(u.deg), new_minor.to(u.deg), new_pa
def getmaxbeam(files, verbose=False): """Get largest beam """ beams = [] for file in files: header = fits.getheader(file, memmap=True) beam = Beam.from_fits_header(header) beams.append(beam) beams = Beams([beam.major.value for beam in beams] * u.deg, [beam.minor.value for beam in beams] * u.deg, [beam.pa.value for beam in beams] * u.deg) return beams.common_beam()
def get_common_psf(fitsfiles): """ common psf for the list of fits files """ beams = [] bmajes = [] bmines = [] bpas = [] for f in fitsfiles: ih = fits.getheader(f) bmajes.append(ih['BMAJ']) bmines.append(ih['BMIN']) bpas.append(ih['BPA']) beam = Beam.from_fits_header(ih) beams.append(beam) beams = Beams(bmajes * u.deg, bmines * u.deg, bpas * u.deg) common = beams.common_beam() smallest = beams.smallest_beam() logging.info('Smallest PSF: %s', smallest) logging.info('Common PSF: %s', common) return common
def getmaxbeam(data_dict, band, cutoff=15 * u.arcsec, tolerance=0.0001, nsamps=200, epsilon=0.0005, verbose=False, debug=False): """Find common beam. Arguments: data_dict {dict} -- Dict containing fits files. band {int} -- ATCA band name. Keyword Arguments: tolerance {float} -- See common_beam (default: {0.0001}) nsamps {int} -- See common_beam (default: {200}) epsilon {float} -- See common_beam (default: {0.0005}) verbose {bool} -- Verbose output (default: {False}) debug {bool} -- Show dubugging plots (default: {False}) Returns: beam_dict {dict} -- Beam and frequency data. """ files = data_dict[band] stokes = ['i', 'q', 'u', 'v'] beam_dict = {} for stoke in stokes: beams = [] freqs = [] for file in files[stoke]: header = fits.getheader(file, memmap=True) freqs.append(header['CRVAL3']) beam = Beam.from_fits_header(header) beams.append(beam) beams = Beams([beam.major.value for beam in beams] * u.deg, [beam.minor.value for beam in beams] * u.deg, [beam.pa.value for beam in beams] * u.deg) flags = beams.major > cutoff beam_dict.update({ stoke + '_beams': beams, stoke + '_freqs': np.array(freqs) * u.Hz, stoke + '_flags': flags }) if debug: plt.figure() plt.title(band) for stoke in stokes: idx = [not flag for flag in beam_dict[stoke + '_flags']] plt.plot(beam_dict[stoke + '_freqs'][idx], beam_dict[stoke + '_beams'].major.to(u.arcsec)[idx], '.', alpha=0.5, label=stoke + '--BMAJ') plt.plot(beam_dict[stoke + '_freqs'][idx], beam_dict[stoke + '_beams'].minor.to(u.arcsec)[idx], '.', alpha=0.5, label=stoke + '--BMIN') plt.xlabel('Frequency [Hz]') plt.ylabel('Beam size [arcsec]') plt.legend() plt.show() bmaj = [] bmin = [] bpa = [] for stoke in stokes: bmaj += list(beam_dict[f'{stoke}_beams'].major. value[~beam_dict[f'{stoke}_flags']]) bmin += list(beam_dict[f'{stoke}_beams'].minor. value[~beam_dict[f'{stoke}_flags']]) bpa += list( beam_dict[f'{stoke}_beams'].pa.value[~beam_dict[f'{stoke}_flags']]) big_beams = Beams(bmaj * u.deg, bmin * u.deg, bpa * u.deg) try: cmn_beam = big_beams.common_beam(tolerance=tolerance, epsilon=epsilon, nsamps=nsamps) except BeamError: if verbose: print("Couldn't find common beam with defaults") print("Trying again with smaller tolerance") cmn_beam = big_beams.common_beam(tolerance=tolerance * 0.1, epsilon=epsilon, nsamps=nsamps) cmn_beam = Beam( major=my_ceil(cmn_beam.major.to(u.arcsec).value, precision=1) * u.arcsec, minor=my_ceil(cmn_beam.minor.to(u.arcsec).value, precision=1) * u.arcsec, pa=round_up(cmn_beam.pa.to(u.deg), decimals=2)) target_header = fits.getheader(data_dict[band]['i'][0], memmap=True) dx = target_header['CDELT1'] * -1 * u.deg dy = target_header['CDELT2'] * u.deg grid = dy conbeams = [cmn_beam.deconvolve(beam) for beam in big_beams] # Check that convolving beam will be nyquist sampled min_samps = [] for b_idx, conbeam in enumerate(conbeams): # Get maj, min, pa samp = conbeam.minor / grid.to(u.arcsec) if samp < 2: min_samps.append([samp, b_idx]) if len(min_samps) > 0: print('Adjusting common beam to be sampled by grid!') worst_idx = np.argmin([samp[0] for samp in min_samps], axis=0) samp_cor_fac, idx = 2 / \ min_samps[worst_idx][0], int( min_samps[worst_idx][1]) conbeam = conbeams[idx] major = conbeam.major minor = conbeam.minor * samp_cor_fac pa = conbeam.pa # Check for small major! if major < minor: major = minor pa = 0 * u.deg cor_beam = Beam(major, minor, pa) if verbose: print('Smallest common beam is:', cmn_beam) cmn_beam = big_beams[idx].convolve(cor_beam) cmn_beam = Beam( major=my_ceil(cmn_beam.major.to(u.arcsec).value, precision=1) * u.arcsec, minor=my_ceil(cmn_beam.minor.to(u.arcsec).value, precision=1) * u.arcsec, pa=round_up(cmn_beam.pa.to(u.deg), decimals=2)) if verbose: print('Smallest common Nyquist sampled beam is:', cmn_beam) if debug: from matplotlib.patches import Ellipse pixscale = 1 * u.arcsec fig = plt.figure() ax = plt.gca() for beam in big_beams: ellipse = Ellipse( (0, 0), width=(beam.major.to(u.deg) / pixscale).to( u.dimensionless_unscaled).value, height=(beam.minor.to(u.deg) / pixscale).to( u.dimensionless_unscaled).value, # PA is 90 deg offset from x-y axes by convention # (it is angle from NCP) angle=(beam.pa + 90 * u.deg).to(u.deg).value, edgecolor='k', fc='None', lw=1, alpha=0.1) ax.add_artist(ellipse) ellipse = Ellipse( (0, 0), width=(cmn_beam.major.to(u.deg) / pixscale).to( u.dimensionless_unscaled).value, height=(cmn_beam.minor.to(u.deg) / pixscale).to( u.dimensionless_unscaled).value, # PA is 90 deg offset from x-y axes by convention # (it is angle from NCP) angle=(cmn_beam.pa + 90 * u.deg).to(u.deg).value, edgecolor='r', fc='None', lw=2, alpha=1, ) ax.add_artist(ellipse) label = f"BMAJ={cmn_beam.major.to(u.arcsec).round()}, BMIN={cmn_beam.minor.to(u.arcsec).round()}, BPA={cmn_beam.pa.to(u.deg).round()}" plt.plot([np.nan], [np.nan], 'r', label=label) plt.xlim(-0.2 * 60, 0.2 * 60) plt.ylim(-0.2 * 60, 0.2 * 60) plt.xlabel('$\Delta$ RA [arcsec]') plt.ylabel('$\Delta$ DEC [arcsec]') plt.legend() plt.show() beam_dict.update({'common_beam': cmn_beam}) return beam_dict
else: raise FileNotFoundError( f'SI error image {si_err_image} does not exist.') ##################################################### # find the smallest common beam if args.beam is None: if args.circbeam: maxmaj = np.max([b[0] for b in all_beams]) target_beam = [maxmaj * 1.01, maxmaj * 1.01, 0.] # add 1% to prevent crash in convolution else: from radio_beam import Beams my_beams = Beams([b[0] for b in all_beams] * u.deg, [b[1] for b in all_beams] * u.deg, [b[2] for b in all_beams] * u.deg) common_beam = my_beams.common_beam() target_beam = [ common_beam.major.value, common_beam.minor.value, common_beam.pa.value ] else: target_beam = [ args.beam[0] / 3600., args.beam[1] / 3600., args.beam[2] ] logging.info('Final beam: %.1f" %.1f" (pa %.1f deg)' \ % (target_beam[0]*3600., target_beam[1]*3600., target_beam[2])) ##################################################### # Generate regrid headers rwcs = pywcs(naxis=2)
def getmaxbeam(file_dict, tolerance=0.0001, nsamps=200, epsilon=0.0005, verbose=False): """Find common beam Arguments: file_dict {dict} -- Filenames for each bandcube. Keyword Arguments: tolerance {float} -- See common_beam (default: {0.0001}) nsamps {int} -- See common_beam (default: {200}) epsilon {float} -- See common_beam (default: {0.0005}) verbose {bool} -- Verbose output (default: {False}) Returns: cmn_beam {Beam} -- Common beam """ if verbose: print('Finding common beam...') stokes = ['i', 'q', 'u', 'v'] beam_dict = {} beams = [] for stoke in stokes: for i, file in enumerate(file_dict[stoke]): header = fits.getheader(file, memmap=True) if stoke == 'i' and i == 0: target_header = header beam = Beam.from_fits_header(header) beams.append(beam) beams = Beams([beam.major.value for beam in beams] * u.deg, [beam.minor.value for beam in beams] * u.deg, [beam.pa.value for beam in beams] * u.deg) try: cmn_beam = beams.common_beam(tolerance=tolerance, epsilon=epsilon, nsamps=nsamps) except BeamError: if verbose: print("Couldn't find common beam with defaults") print("Trying again with smaller tolerance") cmn_beam = beams.common_beam(tolerance=tolerance * 0.1, epsilon=epsilon, nsamps=nsamps) cmn_beam = Beam( major=my_ceil(cmn_beam.major.to(u.arcsec).value, precision=0) * u.arcsec, minor=my_ceil(cmn_beam.minor.to(u.arcsec).value, precision=0) * u.arcsec, pa=round_up(cmn_beam.pa.to(u.deg), decimals=2)) dx = target_header['CDELT1'] * -1 * u.deg dy = target_header['CDELT2'] * u.deg assert abs(dx) == abs(dy) grid = dy conbeams = [cmn_beam.deconvolve(beam) for beam in beams] # Check that convolving beam will be nyquist sampled min_samps = [] for b_idx, conbeam in enumerate(conbeams): # Get maj, min, pa samp = conbeam.minor / grid.to(u.arcsec) if samp < 2: min_samps.append([samp, b_idx]) if len(min_samps) > 0: print('Adjusting common beam to be sampled by grid!') worst_idx = np.argmin([samp[0] for samp in min_samps], axis=0) samp_cor_fac, idx = 2 / \ min_samps[worst_idx][0], int( min_samps[worst_idx][1]) conbeam = conbeams[idx] major = conbeam.major minor = conbeam.minor * samp_cor_fac pa = conbeam.pa # Check for small major! if major < minor: major = minor pa = 0 * u.deg cor_beam = Beam(major, minor, pa) if verbose: print('Smallest common beam is:', cmn_beam) cmn_beam = beams[idx].convolve(cor_beam) cmn_beam = Beam( major=my_ceil(cmn_beam.major.to(u.arcsec).value, precision=1) * u.arcsec, minor=my_ceil(cmn_beam.minor.to(u.arcsec).value, precision=1) * u.arcsec, pa=round_up(cmn_beam.pa.to(u.deg), decimals=2)) if verbose: print('Smallest common Nyquist sampled beam is:', cmn_beam) return cmn_beam