def get_polgains_scan_cal2(i, n, scan, reference, sites, method, pad_amp, caltable, show_solution, msgtype): if n > 1: global counter counter.increment() obsh.prog_msg(counter.value(), counter.maxval, msgtype, counter.value() - 1) return polgains_cal_scan(scan, reference, sites, method=method, caltable=caltable, show_solution=show_solution, pad_amp=pad_amp, msgtype=msgtype)
def get_selfcal_scan_cal2(i, n, scan, im, V_scan, sites, polrep, pol, method, minimizer_method, show_solution, pad_amp, gain_tol, caltable, debias, msgtype): if n > 1: global counter counter.increment() obsh.prog_msg(counter.value(), counter.maxval, msgtype, counter.value() - 1) return self_cal_scan(scan, im, V_scan=V_scan, sites=sites, polrep=polrep, pol=pol, method=method, minimizer_method=minimizer_method, show_solution=show_solution, pad_amp=pad_amp, gain_tol=gain_tol, caltable=caltable, debias=debias)
def get_network_scan_cal2(i, n, scan, zbl, sites, cluster_data, polrep, pol, method, pad_amp, gain_tol, caltable, show_solution, debias, msgtype): if n > 1: global counter counter.increment() obsh.prog_msg(counter.value(), counter.maxval, msgtype, counter.value() - 1) return network_cal_scan(scan, zbl, sites, cluster_data, polrep=polrep, pol=pol, zbl_uvidst_max=ZBLCUTOFF, method=method, caltable=caltable, show_solution=show_solution, pad_amp=pad_amp, gain_tol=gain_tol, debias=debias)
def polgains_cal(obs, reference='AA', sites=[], method='phase', minimizer_method='BFGS', pad_amp=0., solution_interval=0.0, scan_solutions=False, caltable=False, processes=-1, show_solution=False, msgtype='bar'): # TODO: function to globalize the polarimetric solution in time # given provided absolute calibration of the reference station # so that we have a meaningful EVPA """Polarimeteric-phase-gains-calibrate a dataset. Numerically solves for polarimetric gains to align RCP and LCP feeds. Uses all baselines to find the solution. Effectively assumes phase of Stokes V to be zero. Because fits are local, it's not providing absolute phase calibration. Args: obs (Obsdata): The observation to be calibrated reference (str): station used as reference to break the degeneracy (LCP on baselines to the reference station remains unchanged) sites (list): list of sites to include in the polarimetric calibration. Empty list calibrates all sites method (str): chooses what to calibrate, 'phase' or 'both' 'phase' is default, most useful (instrumental offsets), 'both' will align RCP/LCP amplitudes as well minimizer_method (str): Method for scipy.optimize.minimize (e.g., 'CG', 'BFGS') pad_amp (float): adds fractional uncertainty to amplitude sigmas in quadrature solution_interval (float): solution interval in seconds; one gain is derived per interval. If 0.0, a solution is determined for each unique time. scan_solutions (bool): If True, determine one gain per site per scan Supersedes solution_interval. caltable (bool): if True, returns a Caltable instead of an Obsdata processes (int): number of cores to use in multiprocessing show_solution (bool): if True, display the solution as it is calculated msgtype (str): type of progress message to be printed, default is 'bar' Returns: (Obsdata): the calibrated observation, if caltable==False (Caltable): the derived calibration table, if caltable==True """ # circular representation is needed if obs.polrep != 'circ': obs = obs.switch_polrep('circ') if len(sites) == 0: print("No stations specified in polgain cal!") print( 'Defaulting to calibrating all stations with reference station as: ' + reference) sites = np.array([x for x in obs.tarr['site'] if x != reference], dtype='<U32') # get scans scans = obs.tlist(t_gather=solution_interval, scan_gather=scan_solutions) scans_cal = copy.copy(scans) # Make the pool for parallel processing if processes > 0: counter = parloop.Counter(initval=0, maxval=len(scans)) if processes > len(scans): processes = len(scans) print("Using Multiprocessing with %d Processes" % processes) pool = Pool(processes=processes, initializer=init, initargs=(counter, )) elif processes == 0: counter = parloop.Counter(initval=0, maxval=len(scans)) processes = int(cpu_count()) if processes > len(scans): processes = len(scans) print("Using Multiprocessing with %d Processes" % processes) pool = Pool(processes=processes, initializer=init, initargs=(counter, )) else: print("Not Using Multiprocessing") # loop over scans and calibrate tstart = time.time() if processes > 0: # with multiprocessing scans_cal = pool.map(get_polgains_scan_cal, [[ i, len(scans), scans[i], reference, sites, method, pad_amp, caltable, show_solution, msgtype ] for i in range(len(scans))]) else: # without multiprocessing for i in range(len(scans)): obsh.prog_msg(i, len(scans), msgtype=msgtype, nscan_last=i - 1) scans_cal[i] = polgains_cal_scan(scans[i], reference, sites, method=method, minimizer_method=minimizer_method, show_solution=show_solution, caltable=caltable, pad_amp=pad_amp) tstop = time.time() print("\npolgain_cal time: %f s" % (tstop - tstart)) if caltable: # create and return a caltable allsites = obs.tarr['site'] caldict = {k: v.reshape(1) for k, v in scans_cal[0].items()} for i in range(1, len(scans_cal)): row = scans_cal[i] if len(row) == 0: continue for site in allsites: try: dat = row[site] except KeyError: continue try: caldict[site] = np.append(caldict[site], row[site]) except KeyError: caldict[site] = [dat] caltable = ehtim.caltable.Caltable(obs.ra, obs.dec, obs.rf, obs.bw, caldict, obs.tarr, source=obs.source, mjd=obs.mjd, timetype=obs.timetype) out = caltable else: # return the calibrated observation arglist, argdict = obs.obsdata_args() arglist[4] = np.concatenate(scans_cal) out = ehtim.obsdata.Obsdata(*arglist, **argdict) # close multiprocessing jobs if processes != -1: pool.close() return out
def self_cal(obs, im, sites=[], method="both", pol='I', minimizer_method='BFGS', pad_amp=0., gain_tol=.2, solution_interval=0.0, scan_solutions=False, ttype='direct', fft_pad_factor=2, caltable=False, debias=True, copy_closure_tables=True, processes=-1, show_solution=False, msgtype='bar'): """Self-calibrate a dataset to an image. Args: obs (Obsdata): The observation to be calibrated im (Image): the image to be calibrated to sites (list): list of sites to include in the self calibration. empty list calibrates all sites method (str): chooses what to calibrate, 'amp', 'phase', or 'both' minimizer_method (str): Method for scipy.optimize.minimize (e.g., 'CG', 'BFGS') pol (str): which image polarization to self-calibrate visibilities to pad_amp (float): adds fractional uncertainty to amplitude sigmas in quadrature gain_tol (float or list): gains that exceed this value will be disfavored by the prior for asymmetric gain_tol for corrections below/above unity, pass a 2-element list solution_interval (float): solution interval in seconds; If 0., determine solution for each unique time scan_solutions (bool): If True, determine one gain per site per scan (supersedes solution_interval) caltable (bool): if True, returns a Caltable instead of an Obsdata processes (int): number of cores to use in multiprocessing ttype (str): if "fast" or "nfft" use FFT to produce visibilities. Else "direct" for DTFT fft_pad_factor (float): zero pad the image to fft_pad_factor * image size in FFT debias (bool): If True, debias the amplitudes show_solution (bool): if True, display the solution as it is calculated msgtype (str): type of progress message to be printed, default is 'bar' Returns: (Obsdata): the calibrated observation, if caltable==False (Caltable): the derived calibration table, if caltable==True """ if pol not in ['I', 'Q', 'U', 'V', 'RR', 'LL']: raise Exception( "Can only self-calibrate to I, Q, U, V, RR, or LL images!") if pol in ['I', 'Q', 'U', 'V']: if obs.polrep != 'stokes': raise Exception( "selfcal pol is a stokes parameter, but obs.polrep!='stokes'") im = im.switch_polrep('stokes', pol) elif pol in ['RR', 'LL', 'RRLL']: if obs.polrep != 'circ': raise Exception("selfcal pol is RR or LL, but obs.polrep!='circ'") im = im.switch_polrep('circ', pol) # V = model visibility, V' = measured visibility, G_i = site gain # G_i * conj(G_j) * V_ij = V'_ij if len(sites) == 0: print( "No stations specified in self cal: defaulting to calibrating all stations!" ) sites = obs.tarr['site'] # First, sample the model visibilities of the specified polarization print("Computing the Model Visibilities with " + ttype + " Fourier Transform...") obs_clean = im.observe_same_nonoise(obs, ttype=ttype, fft_pad_factor=fft_pad_factor) # Partition the list of observed visibilities into scans scans = obs.tlist(t_gather=solution_interval, scan_gather=scan_solutions) scans_cal = copy.copy(scans) # Partition the list of model visibilities into scans V_scans = [ o[ehc.vis_poldict[pol]] for o in obs_clean.tlist(t_gather=solution_interval, scan_gather=scan_solutions) ] # Make the pool for parallel processing if processes > 0: counter = parloop.Counter(initval=0, maxval=len(scans)) print("Using Multiprocessing with %d Processes" % processes) pool = Pool(processes=processes, initializer=init, initargs=(counter, )) elif processes == 0: counter = parloop.Counter(initval=0, maxval=len(scans)) processes = int(cpu_count()) print("Using Multiprocessing with %d Processes" % processes) pool = Pool(processes=processes, initializer=init, initargs=(counter, )) else: print("Not Using Multiprocessing") # loop over scans and calibrate tstart = time.time() if processes > 0: # run on multiple cores with multiprocessing scans_cal = np.array( pool.map(get_selfcal_scan_cal, [[ i, len(scans), scans[i], im, V_scans[i], sites, obs.polrep, pol, method, minimizer_method, show_solution, pad_amp, gain_tol, caltable, debias, msgtype ] for i in range(len(scans))])) else: # run on a single core for i in range(len(scans)): obsh.prog_msg(i, len(scans), msgtype=msgtype, nscan_last=i - 1) scans_cal[i] = self_cal_scan(scans[i], im, V_scan=V_scans[i], sites=sites, polrep=obs.polrep, pol=pol, method=method, minimizer_method=minimizer_method, show_solution=show_solution, debias=debias, pad_amp=pad_amp, gain_tol=gain_tol, caltable=caltable) tstop = time.time() print("\nself_cal time: %f s" % (tstop - tstart)) if caltable: # assemble the caltable to return allsites = obs.tarr['site'] caldict = scans_cal[0] for i in range(1, len(scans_cal)): row = scans_cal[i] for site in allsites: try: dat = row[site] except KeyError: continue try: caldict[site] = np.append(caldict[site], row[site]) except KeyError: caldict[site] = dat caltable = ehtim.caltable.Caltable(obs.ra, obs.dec, obs.rf, obs.bw, caldict, obs.tarr, source=obs.source, mjd=obs.mjd, timetype=obs.timetype) out = caltable else: # return a calibrated observation arglist, argdict = obs.obsdata_args() arglist[4] = np.concatenate(scans_cal) out = ehtim.obsdata.Obsdata(*arglist, **argdict) if copy_closure_tables: out.camp = obs.camp out.logcamp = obs.logcamp out.cphase = obs.cphase # close multiprocessing jobs if processes >= 0: pool.close() return out
def network_cal(obs, zbl, sites=[], zbl_uvdist_max=ZBLCUTOFF, method="amp", minimizer_method='BFGS', pol='I', pad_amp=0., gain_tol=.2, solution_interval=0.0, scan_solutions=False, caltable=False, processes=-1, show_solution=False, debias=True, msgtype='bar'): """Network-calibrate a dataset with zero baseline constraints. Args: obs (Obsdata): The observation to be calibrated zbl (float or function): constant zero baseline flux in Jy, or a function of UT hour. sites (list): list of sites to include in the network calibration. empty list calibrates all sites zbl_uvdist_max (float): maximum uv-distance considered a zero baseline method (str): chooses what to calibrate, 'amp', 'phase', or 'both'. minimizer_method (str): Method for scipy.optimize.minimize (e.g., 'CG', 'BFGS') pol (str): which visibility to compute gains for pad_amp (float): adds fractional uncertainty to amplitude sigmas in quadrature gain_tol (float): gains that exceed this value will be disfavored by the prior solution_interval (float): solution interval in seconds; one gain is derived for each interval. If 0.0, a solution is determined for each unique time scan_solutions (bool): If True, determine one gain per site per scan. Supersedes solution_interval debias (bool): If True, debias the amplitudes caltable (bool): if True, returns a Caltable instead of an Obsdata processes (int): number of cores to use in multiprocessing show_solution (bool): if True, display the solution as it is calculated msgtype (str): type of progress message to be printed, default is 'bar' Returns: (Obsdata): the calibrated observation, if caltable==False (Caltable): the derived calibration table, if caltable==True """ # Here, RRLL means to use both RR and LL (both as proxies for Stokes I) # to derive a network calibration solution if pol not in ['I', 'Q', 'U', 'V', 'RR', 'LL', 'RRLL']: raise Exception( "Can only network-calibrate to I, Q, U, V, RR, LL, or RRLL!") if pol in ['I', 'Q', 'U', 'V']: if obs.polrep != 'stokes': raise Exception( "netcal pol is a stokes parameter, but obs.polrep!='stokes'") # obs = obs.switch_polrep('stokes',pol) elif pol in ['RR', 'LL', 'RRLL']: if obs.polrep != 'circ': raise Exception( "netcal pol is RR or LL or RRLL, but obs.polrep!='circ'") # obs = obs.switch_polrep('circ',pol) # V = model visibility, V' = measured visibility, G_i = site gain # G_i * conj(G_j) * V_ij = V'_ij if len(sites) == 0: print( "No stations specified in network cal: defaulting to calibrating all stations!" ) sites = obs.tarr['site'] # find colocated sites and put into list allclusters cluster_data = calh.make_cluster_data(obs, zbl_uvdist_max) # get scans scans = obs.tlist(t_gather=solution_interval, scan_gather=scan_solutions) scans_cal = copy.copy(scans) # Make the pool for parallel processing if processes > 0: counter = parloop.Counter(initval=0, maxval=len(scans)) if processes > len(scans): processes = len(scans) print("Using Multiprocessing with %d Processes" % processes) pool = Pool(processes=processes, initializer=init, initargs=(counter, )) elif processes == 0: counter = parloop.Counter(initval=0, maxval=len(scans)) processes = int(cpu_count()) if processes > len(scans): processes = len(scans) print("Using Multiprocessing with %d Processes" % processes) pool = Pool(processes=processes, initializer=init, initargs=(counter, )) else: print("Not Using Multiprocessing") # loop over scans and calibrate tstart = time.time() if processes > 0: # with multiprocessing scans_cal = pool.map(get_network_scan_cal, [[ i, len(scans), scans[i], zbl, sites, cluster_data, obs.polrep, pol, method, pad_amp, gain_tol, caltable, show_solution, debias, msgtype ] for i in range(len(scans))]) else: # without multiprocessing for i in range(len(scans)): obsh.prog_msg(i, len(scans), msgtype=msgtype, nscan_last=i - 1) scans_cal[i] = network_cal_scan(scans[i], zbl, sites, cluster_data, polrep=obs.polrep, pol=pol, method=method, minimizer_method=minimizer_method, show_solution=show_solution, caltable=caltable, pad_amp=pad_amp, gain_tol=gain_tol, debias=debias) tstop = time.time() print("\nnetwork_cal time: %f s" % (tstop - tstart)) if caltable: # create and return a caltable allsites = obs.tarr['site'] caldict = {k: v.reshape(1) for k, v in scans_cal[0].items()} for i in range(1, len(scans_cal)): row = scans_cal[i] if len(row) == 0: continue for site in allsites: try: dat = row[site] except KeyError: continue try: caldict[site] = np.append(caldict[site], row[site]) except KeyError: caldict[site] = [dat] caltable = ehtim.caltable.Caltable(obs.ra, obs.dec, obs.rf, obs.bw, caldict, obs.tarr, source=obs.source, mjd=obs.mjd, timetype=obs.timetype) out = caltable else: # return the calibrated observation arglist, argdict = obs.obsdata_args() arglist[4] = np.concatenate(scans_cal) out = ehtim.obsdata.Obsdata(*arglist, **argdict) # close multiprocessing jobs if processes != -1: pool.close() return out