def check_recombine(obsID, directory=None, required_size=327680000, \ required_size_ics=30720000, startsec=None, n_secs=None): ''' Checks that the number of files in directory (/astro/mwaops/vcs/[obsID]/combined/) is .... as that found on the archive and also checks that all files have the same size (327680000 by default). ''' if not directory: directory = "/astro/mwaops/vcs/{0}/combined/".format(obsID) base = "\n Checking file size and number of files for obsID {0} in {1} for ".format( obsID, directory) n_secs = n_secs if n_secs else 1 logger.info(base + "gps times {0} to {1}".format(startsec, startsec + n_secs - 1) if startsec else base + "the whole time range.") required_size = required_size # we need to get the number of unique seconds from the file names meta = getmeta(service='obs', params={'obs_id': obsID}) files = np.array(list(meta['files'].keys())) mask = np.array(['.dat' in file for file in files]) if not startsec: times = [time[11:21] for time in files[mask]] n_secs = len(set(times)) command = "ls -l %s/*ch*.dat | ((tee /dev/fd/5 | wc -l >/dev/fd/4) 5>&1 | " %(directory) + \ "awk '($5!=%s){print $9}' | tee >> %s/%s_all.txt | xargs rm -rf) 4>&1;" %(required_size, directory, obsID) + \ "cat %s/%s_all.txt; rm -rf %s/%s_all.txt" %(directory, obsID, directory, obsID) output = subprocess.Popen([command], stdout=subprocess.PIPE, shell=True).stdout else: output = subprocess.Popen(["count=0;for sec in `seq -w %s %s `;do let count=${count}+`ls -l %s/*${sec}*ch*.dat | " %(startsec, startsec+n_secs-1, directory) + \ "((tee /dev/fd/5 | wc -l >/dev/fd/4) 5>&1 | awk '($5!=%s) " %(required_size) + \ "{print $9}' | tee >> %s/errors_%s.txt | xargs rm -rf) 4>&1`;done;" %(directory,startsec) +\ "echo ${count}; cat %s/errors_%s.txt;rm -rf %s/errors_%s.txt" %(directory,startsec,directory,startsec)], stdout=subprocess.PIPE, shell=True).stdout output = output.readlines() files_in_dir = int(output[0].strip()) expected_files = n_secs * 25 error = False error, n_ics = check_recombine_ics(directory=directory, \ startsec=startsec, n_secs=n_secs, required_size=required_size_ics) files_in_dir += n_ics if not files_in_dir == expected_files: logger.error("We have {0} files but expected {1}".format( files_in_dir, expected_files)) error = True for line in output[1:]: if b'dat' in line: logger.warning("Deleted {0} due to wrong size.".format( line.strip())) error = True if not error: logger.info("We have all {0} files as expected.".format(files_in_dir)) return error
def get_delay_steps(obs): beam_meta_data = getmeta(service='obs', params={'obs_id':obs}) ra = beam_meta_data[u'metadata'][u'ra_pointing'] dec = beam_meta_data[u'metadata'][u'dec_pointing'] duration = beam_meta_data[u'stoptime'] - beam_meta_data[u'starttime'] #gps time xdelays = beam_meta_data[u'rfstreams'][u"0"][u'xdelays'] minfreq = float(min(beam_meta_data[u'rfstreams'][u"0"][u'frequencies'])) maxfreq = float(max(beam_meta_data[u'rfstreams'][u"0"][u'frequencies'])) channels = beam_meta_data[u'rfstreams'][u"0"][u'frequencies'] centrefreq = 1.28 * (minfreq + (maxfreq - minfreq) / 2) return obs, ra, dec, duration, xdelays, centrefreq, channels
def get_obs_metadata(obs): beam_meta_data = getmeta(service='obs', params={'obs_id': obs}) channels = beam_meta_data[u'rfstreams'][u"0"][u'frequencies'] freqs = [float(c) * 1.28 for c in channels] xdelays = beam_meta_data[u'rfstreams'][u"0"][u'xdelays'] #pythodelays = beam_meta_data[u'rfstreams'][u"0"][u'xdelays'] ydelays = beam_meta_data[u'rfstreams'][u"0"][u'ydelays'] _, pointing_AZ, pointing_ZA = mwa_alt_az_za(obs) return { "channels": channels, "frequencies": freqs, "xdelays": xdelays, "ydelays": ydelays, "az": pointing_AZ, "za": pointing_ZA }
def get_files_and_sizes(obsID, mode): if mode == 'raw': suffix = '.dat' elif mode == 'tar_ics': suffix = '.tar' elif mode == 'ics': suffix = '_ics.dat' else: logger.error("Wrong mode supplied. Options are raw, tar_ics, and ics") return logger.info( "Retrieving file info from MWA database for all {0} files...".format( suffix)) meta = getmeta(service='obs', params={'obs_id': obsID, 'nocache': 1}) # 'nocache' is used above so we get don't use the cached metadata as that could # be out of data so we force it to get up to date values files = np.array(list(meta['files'].keys())) files_masked = [] sizes = [] for f in files: if suffix in f: sizes.append(meta['files'][f]['size']) files_masked.append(f) #mask = np.array([suffix in file for file in files]) #files = files[mask] #sizes=np.array([meta['files'][f]['size'] for f in files]) logger.info( "...Done. Expect all on database to be {0} bytes in size...".format( sizes[0])) size_check = True for s in sizes: if not s == sizes[0]: size_check = False #if np.all(sizes == sizes[0]): #this stopped working for some reason if size_check: logger.info("...yep they are. Now checking on disk.") return files_masked, suffix, sizes[0] else: logger.error("Not all files have the same size. Check your data!") logger.error("{0}".format(np.vstack((files, sizes)).T)) return
def find_obsids_meta_pages(params={'mode': 'VOLTAGE_START'}): """ Loops over pages for each page for MWA metadata calls """ obsid_list = [] temp = [] page = 1 #need to ask for a page of results at a time while len(temp) == 200 or page == 1: params['page'] = page logger.debug("Page: {0} params: {1}".format(page, params)) temp = getmeta(service='find', params=params) if temp is not None: # if there are non obs in the field (which is rare) None is returned for row in temp: obsid_list.append(row[0]) else: temp = [] page += 1 return obsid_list
def construct_base_string(self): """Construct the basic string to be written to the RTS config file. This string will then be edit with regexp to update the relevant details. Returns ------- file_str : str The base string to be written to an RTS configuration srcipt after manipulation. Raises ------ CalibrationError When there is a problem with some of the observation information and/or its manipulation. """ # get calibrator observation information from database logger.info("Querying metadata database for obsevation information...") obsinfo = getmeta(service='obs', params={'obs_id': str(self.cal_obsid)}) # quick check to make sure what's returned is actually real data if obsinfo[u'metadata'] is None: errmsg = "Metadata database error (metadata empty). Maybe an invalid obs ID?" logger.error(errmsg) raise CalibrationError(errmsg) # get the RA/Dec pointing for the primary beam ra_pointing_degs = obsinfo['metadata']['ra_pointing'] dec_pointing_degs = obsinfo['metadata']['dec_pointing'] # now get the absolute channels self.channels = obsinfo[u'rfstreams'][u"0"][u'frequencies'] # and figure out the MaxFrequency parameter self.max_frequency = 1.28 * ( max(self.channels) + 1 ) # this way we ensure that MaxFrequency is applicable for ALL subbands # use the same operations as in timeconvert.py for our specific need logger.info("Converting times with astropy") #mwa_loc = EarthLocation.of_site('Murchison Widefield Array') mwa_loc = EarthLocation.from_geodetic(lon="116:40:14.93", lat="-26:42:11.95", height=377.8) #Astropy formating utctime = strptime(self.utctime, '%Y%m%d%H%M%S') a_time = strftime('%Y-%m-%dT%H:%M:%S', utctime) obstime = Time(a_time, format='fits', scale='utc', location=mwa_loc) lst_in_hours = obstime.sidereal_time('apparent').hourangle jd = obstime.jd logger.info(" LST: {0}".format(lst_in_hours)) logger.info(" JD : {0}".format(jd)) # set the HA of the image centre to the primary beam HA logger.debug("Determining HA and DEC for primary beam") self.JD = jd pb_ha_hours = (ra_pointing_degs / 360.0) * 24.0 self.PB_HA = lst_in_hours - pb_ha_hours self.PB_DEC = dec_pointing_degs logger.debug("Primary beam: HA = {0} hrs, Dec = {1} deg".format( self.PB_HA, self.PB_DEC)) logger.debug("JD = {0}".format(self.JD)) # get the lowest frequency channel freqs = obsinfo['rfstreams']['0']['frequencies'] start_channel = freqs[0] self.freq_base = start_channel * 1.28e6 - 0.64e6 + 15e3 # frequency base in Hz (based on Steve Ord's logic) self.freq_base = self.freq_base / 1.0e6 # convert to MHz logger.debug("Frequency lower edge = {0} MHz".format(self.freq_base)) # make metafits file formatted for RTS self.metafits_RTSform = self.metafits.split("_metafits_ppds.fits")[0] logger.debug("RTS form metafits location: {0}".format( self.metafits_RTSform)) # create the final file string, expanding symlinks to real paths logger.info("Constructing base RTS configuration script content") file_str = """ ReadAllFromSingleFile= BaseFilename={base}/*_gpubox ReadGpuboxDirect={read_direct} UseCorrelatorInput={use_corr_input} ReadMetafitsFile=1 MetafitsFilename={metafits_file} DoCalibration= doMWArxCorrections=1 doRawDataCorrections=1 doRFIflagging=0 useFastPrimaryBeamModels={beam_model_bool} generateDIjones=1 applyDIcalibration=1 UsePacketInput=0 UseThreadedVI=1 MaxFrequency={max_freq} ObservationFrequencyBase={base_freq} ObservationTimeBase={base_time} ObservationPointCentreHA={obs_ha} ObservationPointCentreDec={obs_dec} ChannelBandwidth={fcbw} NumberOfChannels={nchan} CorrDumpsPerCadence={corr_dumps_per_cadence} CorrDumpTime={corr_dump_time} NumberOfIntegrationBins={n_int_bins} NumberOfIterations=1 StartProcessingAt=0 ArrayPositionLat={array_lat} ArrayPositionLong={array_lon} ArrayNumberOfStations=128 ArrayFile= SourceCatalogueFile={source_list} NumberOfCalibrators=1 NumberOfSourcesToPeel=0 calBaselineMin=20.0 calShortBaselineTaper=40.0 FieldOfViewDegrees=1""".format(base=self.data_dir, read_direct=self.readDirect, use_corr_input=self.useCorrInput, metafits_file=self.metafits_RTSform, beam_model_bool=self.beam_model_bool, max_freq=self.max_frequency, base_freq=self.freq_base, base_time=self.JD, obs_ha=self.PB_HA, obs_dec=self.PB_DEC, fcbw=self.fine_cbw, nchan=self.nfine_chan, corr_dumps_per_cadence=self.n_dumps_to_average, corr_dump_time=self.corr_dump_time, n_int_bins=self.n_integration_bins, array_lat=self.ArrayPositionLat, array_lon=self.ArrayPositionLong, source_list=self.source_list) return file_str
obsid = args.observation # Check if already spliced if glob.glob('{0}/{1}*fits'.format(args.work_dir, args.observation)) and \ not glob.glob('{0}/*_{1}*fits'.format(args.work_dir, args.observation)): print('All files are already spliced so exiting') exit() # Get frequency channels if args.channels: channels = args.channels else: print( "Obtaining metadata from http://mwa-metadata01.pawsey.org.au/metadata/ for OBS ID: " + str(obsid)) beam_meta_data = meta.getmeta(service='obs', params={'obs_id': obsid}) channels = beam_meta_data[u'rfstreams'][u"0"][u'frequencies'] print("Chan order: {}".format(channels)) #hostname = socket.gethostname() hostname = 'temp' if hostname.startswith('john') or hostname.startswith('bryan'): #If on ozstar use their SSD to improve I/O SSD_file_dir = '{}/'.format(os.environ['JOBFS']) print(SSD_file_dir) else: SSD_file_dir = '' # Move into working dir old_dir = os.getcwd() if hostname.startswith('john') or hostname.startswith('bryan'):
def get_files_and_sizes(obsID, mode, mintime=0, maxtime=2000000000): """ Get files and sizes from the MWA metadata server and check that they're all the same size Parameters: ----------- obsID: int The MWA observation ID mode: str The typ of file from 'raw', 'tar_ics' and 'ics' mintime: int The minimum GPS time of observations to check (inclusive, >=) Default: 0 maxtime: int The maximum GPS time of observations to check (exculsive, <) Default: 2000000000 Returns: -------- files_masked, suffix, sizes[0]: list files_masked: list of the files with the input mode/suffix suffix: '.dat', '.tar' or '_ics.dat' depnding on the input mode sizes[0]: size of files in bytes """ if mode == 'raw': suffix = '.dat' elif mode == 'tar_ics': suffix = '.tar' elif mode == 'ics': suffix = '_ics.dat' else: logger.error("Wrong mode supplied. Options are raw, tar_ics, and ics") return logger.info( "Retrieving file info from MWA database for all {0} files...".format( suffix)) files_meta = getmeta(service='data_files', params={ 'obs_id': obsID, 'nocache': 1, 'mintime': mintime, 'maxtime': maxtime }) # 'nocache' is used above so we get don't use the cached metadata as that could # be out of data so we force it to get up to date values files = np.array(list(files_meta.keys())) files_masked = [] sizes = [] for f in files: if suffix in f: sizes.append(files_meta[f]['size']) files_masked.append(f) logger.info( "...Done. Expect all on database to be {0} bytes in size...".format( sizes[0])) size_check = True for s in sizes: if not s == sizes[0]: size_check = False if size_check: logger.info("...yep they are. Now checking on disk.") return files_masked, suffix, sizes[0] else: logger.error("Not all files have the same size. Check your data!") logger.error("{0}".format(np.vstack((files, sizes)).T)) return
def find_sources_in_obs(obsid_list, names_ra_dec, obs_for_source=False, dt_input=100, beam='analytic', min_power=0.3, cal_check=False, all_volt=False, degrees_check=False, metadata_list=None): """ Either creates text files for each MWA obs ID of each source within it or a text file for each source with each MWA obs is that the source is in. Args: obsid_list: list of MWA obs IDs names_ra_dec: [[source_name, ra, dec]] dt: the time step in seconds to do power calculations beam: beam simulation type ['analytic', 'advanced', 'full_EE'] min_power: if above the minium power assumes it's in the beam cal_check: checks the MWA pulsar database if there is a calibration for the obsid all_volt: Use all voltages observations including some inital test data with incorrect formats degrees_check: if false ra and dec is in hms, if true in degrees Output [output_data, obsid_meta]: output_data: The format of output_data is dependant on obs_for_source. If obs_for_source is True: output_data = {jname:[[obsid, duration, enter, exit, max_power], [obsid, duration, enter, exit, max_power]]} If obs_for_source is False: ouput_data = {obsid:[[jname, enter, exit, max_power], [jname, enter, exit, max_power]]} obsid_meta: a list of the output of get_common_obs_metadata for each obsid """ #prepares metadata calls and calculates power powers = [] #powers[obsid][source][time][freq] obsid_meta = [] obsid_to_remove = [] for i, obsid in enumerate(obsid_list): if metadata_list: beam_meta_data, full_meta = metadata_list[i] else: beam_meta_data, full_meta = get_common_obs_metadata(obsid, return_all=True) #beam_meta_data = obsid,ra_obs,dec_obs,time_obs,delays,centrefreq,channels if dt_input * 4 > beam_meta_data[3]: # If the observation time is very short then a smaller dt time is required # to get enough ower imformation dt = int(beam_meta_data[3] / 4.) else: dt = dt_input logger.debug("obsid: {0}, time_obs {1} s, dt {2} s".format(obsid, beam_meta_data[3], dt)) #check for raw volatge files filedata = getmeta(service='data_files', params={'obs_id':obsid, 'nocache':1}) keys = filedata.keys() check = False for k in keys: if '.dat' in k: #TODO check if is still robust check = True if check or all_volt: powers.append(get_beam_power_over_time(beam_meta_data, names_ra_dec, dt=dt, centeronly=True, verbose=False, option=beam, degrees=degrees_check)) obsid_meta.append(beam_meta_data) else: logger.warning('No raw voltage files for %s' % obsid) obsid_to_remove.append(obsid) for otr in obsid_to_remove: obsid_list.remove(otr) #chooses whether to list the source in each obs or the obs for each source output_data = {} if obs_for_source: for sn, source in enumerate(names_ra_dec): source_data = [] for on, obsid in enumerate(obsid_list): source_ob_power = powers[on][sn] if max(source_ob_power) > min_power: duration = obsid_meta[on][3] centre_freq = obsid_meta[on][5] #MHz channels = obsid_meta[on][6] bandwidth = (channels[-1] - channels[0] + 1.)*1.28 #MHz logger.debug("Running beam_enter_exit on obsid: {}".format(obsid)) enter, exit = beam_enter_exit(source_ob_power,duration, dt=dt, min_power=min_power) if enter is not None: source_data.append([obsid, duration, enter, exit, max(source_ob_power)[0], centre_freq, bandwidth]) # For each source make a dictionary key that contains a list of # lists of the data for each obsid output_data[source[0]] = source_data else: #output a list of sorces for each obs for on, obsid in enumerate(obsid_list): duration = obsid_meta[on][3] obsid_data = [] for sn, source in enumerate(names_ra_dec): source_ob_power = powers[on][sn] if max(source_ob_power) > min_power: enter, exit = beam_enter_exit(source_ob_power, duration, dt=dt, min_power=min_power) obsid_data.append([source[0], enter, exit, max(source_ob_power)[0]]) # For each obsid make a dictionary key that contains a list of # lists of the data for each source/pulsar output_data[obsid] = obsid_data return output_data, obsid_meta
def plotSkyMap(obsfile, targetfile, oname, show_psrcat=False, show_mwa_sky=False, show_mwa_unique=False): fig = plt.figure() ax = fig.add_subplot(111, projection="mollweide") mwa_dec_lim = 30 mwa_only_dec_lim = -50 if show_mwa_sky: # Make a patch that is transformable through axis projection # and highlight the visible sky for the MWA Path = mpath.Path ra_start = -np.pi ra_end = np.pi dec_start = -np.pi / 2 dec_end = np.radians(mwa_dec_lim) path_data = [ (Path.MOVETO, (ra_start, dec_start)), (Path.LINETO, (ra_start, dec_end)), (Path.LINETO, (ra_end, dec_end)), (Path.LINETO, (ra_end, dec_start)), (Path.CLOSEPOLY, (ra_end, dec_start)), ] codes, verts = zip(*path_data) path = mpath.Path(verts, codes) patch = mpatches.PathPatch(path, lw=0, ec="lightskyblue", fc="lightskyblue", label="All MWA sky") ax.add_patch(patch) if show_mwa_unique: # Make a patch that is transformable through axis projection # and highlight the part of the sky ONLY visible to the MWA ra_start = -np.pi ra_end = np.pi dec_start = -np.pi / 2 dec_end = np.radians(mwa_only_dec_lim) path_data = [ (Path.MOVETO, (ra_start, dec_start)), (Path.LINETO, (ra_start, dec_end)), (Path.LINETO, (ra_end, dec_end)), (Path.LINETO, (ra_end, dec_start)), (Path.CLOSEPOLY, (ra_end, dec_start)), ] codes, verts = zip(*path_data) path = mpath.Path(verts, codes) patch = mpatches.PathPatch(path, lw=0, ec="lightgreen", fc="lightgreen", label="Unique MWA sky") ax.add_patch(patch) if show_psrcat: # Retrieve the local installed PSRCAT catalogue and plot those pulsar positions cmd = 'psrcat -o short_csv -nocand -nonumber -c "Jname RAJ DECJ" | sed "2d" > psrcat.csv' subprocess.call(cmd, shell=True) psrcat_coords = read_data("psrcat.csv", delim=";") os.remove("psrcat.csv") # Create a mask for pulsar in and out of the declination limit of the MWA maskGood = psrcat_coords.dec.wrap_at(180*u.deg).deg < mwa_dec_lim maskBad = psrcat_coords.dec.wrap_at(180*u.deg).deg >= mwa_dec_lim psrcat_ra_good = -psrcat_coords.ra.wrap_at(180*u.deg).rad[maskGood] # negative because RA increases to the West psrcat_dec_good = psrcat_coords.dec.wrap_at(180*u.deg).rad[maskGood] psrcat_ra_bad = -psrcat_coords.ra.wrap_at(180*u.deg).rad[maskBad] # negative because RA increases to the West psrcat_dec_bad = psrcat_coords.dec.wrap_at(180*u.deg).rad[maskBad] # Now plot the pulsar locations ax.scatter(psrcat_ra_good, psrcat_dec_good, 0.01, marker="x", color="0.4", zorder=1.4) ax.scatter(psrcat_ra_bad, psrcat_dec_bad, 0.01, marker="x", color="0.8", zorder=1.4) # Calculate beam patterns and plot contours levels = np.arange(0.25, 1., 0.05) cmap = plt.get_cmap("cubehelix_r") obsids = read_data(obsfile, coords=False)["OBSID"] for obsid in obsids: #print "Accessing database for observation: {0}".format(obsid) logger.info("Accessing database for observation: {0}".format(obsid)) # TODO: I think this process is now implemented in a function in mwa_metadb_utils, need to double check beam_meta_data = getmeta(service='obs', params={'obs_id': obsid}) ra = beam_meta_data[u'metadata'][u'ra_pointing'] dec = beam_meta_data[u'metadata'][u'dec_pointing'] duration = beam_meta_data[u'stoptime'] - beam_meta_data[u'starttime'] #gps time delays = beam_meta_data[u'rfstreams'][u'0'][u'xdelays'] minfreq = float(min(beam_meta_data[u'rfstreams'][u"0"][u'frequencies'])) maxfreq = float(max(beam_meta_data[u'rfstreams'][u"0"][u'frequencies'])) centrefreq = 1.28e6 * (minfreq + (maxfreq-minfreq)/2) #in MHz channels = beam_meta_data[u'rfstreams'][u"0"][u'frequencies'] metadata = [obsid, ra, dec, duration, delays, centrefreq, channels] beam_meta_data, full_meta = get_common_obs_metadata(obsid, return_all = True) # Create a meshgrid over which to iterate Dec = [] ; RA = [] map_dec_range = np.arange(-89, 90, 3) map_ra_range = np.arange(0, 360, 3) for i in map_dec_range: for j in map_ra_range: Dec.append(i) RA.append(j) RADecIndex = np.arange(len(RA)) names_ra_dec = np.column_stack((RADecIndex, RA, Dec)) #print "Creating beam patterns..." time_intervals = 600 # seconds powout = get_beam_power_over_time(beam_meta_data, names_ra_dec, dt=time_intervals, degrees=True) z=[] ; x=[] ; y=[] for c in range(len(RA)): temppower = 0. for t in range(powout.shape[1]): power_ra = powout[c,t,0] if power_ra > temppower: temppower = power_ra z.append(temppower) if RA[c] > 180: x.append(-RA[c]/180.*np.pi+2*np.pi) else: x.append(-RA[c]/180.*np.pi) y.append(Dec[c]/180.*np.pi) nx=np.array(x) ; ny=np.array(y); nz=np.array(z) #print "Plotting beam pattern contours..." logger.info("Plotting beam pattern contours...") # Set vmin and vmax to ensure the beam patterns are on the same color scale # and plot the beam pattern contours on the map #c = ax.tricontour(wrapped_ra, wrapped_dec, final_pattern_power, levels=levels, cmap=cmap, vmin=levels.min(), vmax=levels.max(), zorder=1.3) c = ax.tricontour(nx, ny, nz, levels=levels, cmap=cmap, vmin=levels.min(), vmax=levels.max(), zorder=1.3) # Make a figure color scale based on the contour sets (which all have the same max/min values fig.colorbar(c, fraction=0.02, pad=0.03, label="Zenith normalised power") # Plot the target positions, using Astropy to wrap at correct RA target_coords = read_data(targetfile) wrapped_target_ra = -target_coords.ra.wrap_at(180*u.deg).rad # negative because RA increases to the West wrapped_target_dec = target_coords.dec.wrap_at(180*u.deg).rad #print "Plotting target source positions..." logger.info("Plotting target source positions...") ax.scatter(wrapped_target_ra, wrapped_target_dec, 10, marker="x", color="red", zorder=1.6, label="Target sources") xtick_labels = ["10h", "8h", "6h", "4h", "2h", "0h", "22h", "20h", "18h", "16h", "14h"] ax.set_xticklabels(xtick_labels, color="0.2") ax.set_xlabel("Right Ascension") ax.set_ylabel("Declination") ax.grid(True, color="k", lw=0.5, ls=":") # Place upper-right corner of legend at specified Axis coordinates ax.legend(loc="upper right", bbox_to_anchor=(1.02, 0.08), numpoints=1, borderaxespad=0., fontsize=6) plt.savefig(oname, format="eps", bbox_inches="tight")