def imstat(): """main logic:""" optlist = parse_args() mu.init_logging(optlist.debug) mu.init_warnings() ncalls.counter = 0 # begin processing -- loop over files for ffile in optlist.fitsfile: try: hdulist = fits.open(ffile) except IOError as ioerr: logging.error("IOError: %s", ioerr) exit(1) if optlist.info: # just print the image info per file hdulist.info() continue if not optlist.noheadings: # print filename print("#") print("# {}".format(os.path.basename(ffile))) # Construct a list of the HDU's to work on hduids = iu.get_requested_image_hduids( hdulist, optlist.hduname, optlist.hduindex ) if optlist.quicklook: quicklook(optlist, hduids, hdulist) else: stats_proc(optlist, hduids, hdulist) ncalls.counter = 0 # reset per file, triggers headers
def imxtalk(): """main logic:""" optlist = parse_args() mu.init_logging(optlist.debug) mu.init_warnings() ncalls.counter = 0 # begin processing -- loop over files for ffile in optlist.fitsfile: try: hdulist = fits.open(ffile) except IOError as ioerr: logging.error("IOError: %s", ioerr) exit(1) if optlist.info: # just print the image info per file hdulist.info() continue # Construct a list of the source HDU's to work on srcids = iu.get_requested_image_hduids(hdulist, optlist.srcname, optlist.srcindex) # Construct a list of the response HDU's to work on rspids = iu.get_requested_image_hduids(hdulist, optlist.rspname, optlist.rspindex) # do stuff logging.debug("calling iu.get_union_of_bad_column_segs(hdulist)") bad_segs = iu.get_union_of_bad_column_segs(hdulist) logging.debug("bad_segs=%s", bad_segs) max_rn = 7.0 pcnt = 20 lsst_num = hdulist[0].header.get("LSST_NUM") # get_xtalk_coefs(hdulist, srcids, rspids, optlist.threshold) for srcid in srcids: hdu_s = hdulist[srcid] # subtract the bias estimate from the source array if lsst_num and re.match(r"^E2V-CCD250", lsst_num): stype = "byrowe2v" else: stype = "byrow" ptype = "bycolfilter" iu.subtract_bias(stype, ptype, hdu_s, bad_segs) logging.info("hdu_s = %s", hdu_s.header["EXTNAME"]) (datasec_s, soscan_s, poscan_s) = iu.get_data_oscan_slices(hdu_s) rn_est = min(np.std(hdu_s.data[poscan_s[0], soscan_s[1]]), max_rn) if optlist.threshold: thresh = optlist.threshold else: rn_est = min(np.std(hdu_s.data[poscan_s[0], soscan_s[1]]), max_rn) thresh = 500 * rn_est # estimate source background level, the threshold will be added to that # since we are only interested in source pixels above background by thresh thresh += np.percentile(hdu_s.data[datasec_s], pcnt) # make the (weights) mask used in response hdu bckgrnd subtraction mask_s = np.ones_like(hdu_s.data, dtype=int) mask_s[np.nonzero(hdu_s.data > thresh)] = 0 mask_s[:, bad_segs] = 0 # fold these in arr_s = hdu_s.data.flatten("K") logging.debug("np.shape(arr_s)= %s", np.shape(arr_s)) arr_x = arr_s[arr_s > thresh] logging.debug("found %d nans in arr_x", np.count_nonzero(np.isnan(arr_x))) arr_x = arr_x.reshape( -1, 1) # infer 1st axis, 2nd axis for 1 "feature" logging.debug("np.shape(arr_x)= %s", np.shape(arr_x)) if np.size(arr_x) < 1000: logging.warn( "not enough source points to produce a coef: %d < 100", np.size(arr_x), ) continue if optlist.plot: plt.style.use(optlist.style) pu.update_rcparams() fig, axes = pu.get_fig_and_axis( len(rspids), optlist.layout, False, optlist.sharex, optlist.sharey, None, ) nprows, npcols = (axes.shape[0], axes.shape[1]) pu.set_fig_title(optlist.title, ffile, fig) sylim_upper = sylim_lower = 0.0 for rindex, rspid in enumerate(rspids): if rspid == srcid: sindex = rindex continue hdu_r = hdulist[rspid] logging.info(" hdu_r = %s", hdu_r.header["EXTNAME"]) if np.shape(hdu_s.data) != np.shape(hdu_r.data): logging.warning( "hdu's %s, %s shapes not commensurate: %s != %s, skipping", hdu_s.header["EXTNAME"], hdu_r.header["EXTNAME"], np.shape(hdu_s.data), np.shape(hdu_r.data), ) continue (datasec_r, soscan_r, poscan_r) = iu.get_data_oscan_slices(hdu_r) iu.subtract_bias(stype, ptype, hdu_r, bad_segs) # need to subtract background level estimate from hdu_s but it may # have lots of structure so need somewhat careful estimate # ------------ # redo this with masking and line by line interp across the mask logging.debug("found %d nans in hdu_r", np.count_nonzero(np.isnan(hdu_r.data))) iu.subtract_background_for_xtalk(hdu_r, mask_s, datasec_r) logging.debug("found %d nans in hdu_r", np.count_nonzero(np.isnan(hdu_r.data))) arr_r = hdu_r.data.flatten("K") logging.debug("np.shape(arr_r)= %s", np.shape(arr_r)) arr_y = arr_r[arr_s > thresh] logging.debug("found %d nans in arr_y", np.count_nonzero(np.isnan(arr_y))) arr_xp = arr_x[~np.isnan(arr_y)] arr_yp = arr_y[~np.isnan(arr_y)] # reject high sources in response channel arr_xp = arr_xp[arr_yp < thresh] arr_yp = arr_yp[arr_yp < thresh] if optlist.intercept: lr = linear_model.LinearRegression() ransac = linear_model.RANSACRegressor() else: lr = linear_model.LinearRegression(fit_intercept=False) ransac = linear_model.RANSACRegressor( linear_model.LinearRegression(fit_intercept=False)) # lr.fit(arr_xp, arr_yp) # ransac.fit(arr_xp, arr_yp) # print(f"lr.coef={lr.coef_}") # print(f"ransac.estimator.coef={ransac.estimator_.coef_}") if np.max(arr_xp) < 0.95 * np.max(arr_x): logging.warning("threshold is too low, raise and re-run") nbins = (np.max(arr_xp) - np.min(arr_xp)) / 1000 * rn_est logging.debug("np.max(arr_xp) = %.2f", np.max(arr_xp)) logging.debug("np.min(arr_xp) = %.2f", np.min(arr_xp)) logging.debug("nbins = %d", nbins) s, edges, _ = binned_statistic(arr_xp[:, 0], arr_yp, "median", nbins) cnt, cedges, _ = binned_statistic(arr_xp[:, 0], arr_yp, "count", nbins) bin_width = edges[1] - edges[0] logging.debug("bin_width = %.2f", bin_width) binx = edges[1:] - bin_width / 2 binx = binx[~np.isnan(s)] # remove the empty bins count = cnt[~np.isnan(s)] logging.debug( "count: mean: %.2f median: %.2f stddev: %.2f min: %.2f max: %.2f", np.mean(count), np.median(count), np.std(count), np.min(count), np.max(count), ) count = np.sqrt(count) * (np.log10(binx) ) # extra weight on high bins logging.debug("binx[-10:] = %s", binx[-10:]) logging.debug("count[-10:] = %s", count[-10:]) s = s[~np.isnan(s)] sylim_upper = np.percentile(arr_yp, 99) sylim_lower = np.percentile(arr_yp, 1) if optlist.raw: # expand limits sydel = sylim_upper - sylim_lower sylim_upper += 0.4 * sydel sylim_lower -= 0.3 * sydel binx = binx.reshape( -1, 1) # infer 1st axis, 2nd axis for 1 "feature" lr.fit(binx, s, sample_weight=count) ransac.fit(binx, s, count) inlier_mask = ransac.inlier_mask_ outlier_mask = np.logical_not(inlier_mask) lrcoef = lr.coef_[0] lrincpt = lr.intercept_ rscoef = ransac.estimator_.coef_[0] rsincpt = ransac.estimator_.intercept_ print(f"binned lr.coef={lrcoef:>3g}") if optlist.intercept: print(f"binned lr.intercept={lrincpt:>.2f}") print(f"binned ransac.estimator.coef={rscoef:>3g}") if optlist.intercept: print(f"binned ransac.estimator.intercept={rsincpt:>.2f}") # plotting if optlist.plot: ax = np.ravel(axes)[int(rindex / npcols) * npcols + rindex % npcols] if optlist.style == "ggplot": ax.scatter([], []) # skip the first color ax.grid(True) ax.set_xlabel("source signal", size="x-small") ax.set_ylabel("response signal", size="x-small") if optlist.raw: ax.scatter(arr_xp[:, 0], arr_yp, s=1.0, label="raw") si = s[inlier_mask] ci = count[inlier_mask] # ci[-1] = 1.0 ax.scatter( binx[inlier_mask, 0], si, # s=np.sqrt(count), s=np.sqrt(ci), color="blue", alpha=0.5, label="inliers", ) si = s[outlier_mask] ci = count[outlier_mask] # ci[-1] = 1.0 ax.scatter( binx[outlier_mask, 0], si, # s=np.sqrt(count), s=np.sqrt(ci), color="purple", alpha=0.5, label="outliers", ) if optlist.predict: # Predict and plot result of estimated models line_x = np.arange(0.0, binx.max())[:, np.newaxis] line_y = lr.predict(line_x) line_y_ransac = ransac.predict(line_x) lw = 2 if optlist.intercept: lbl = f"lr: {lrcoef:>.3g}*x + {lrincpt:>.3g}" else: lbl = f"lr: {lrcoef:>.3g}*x" ax.plot(line_x, line_y, color="navy", linewidth=lw, label=lbl) if optlist.intercept: lbl = f"ransac: {rscoef:>.3g}*x + {rsincpt:>.3g}" else: lbl = f"ransac: {rscoef:>.3g}*x" ax.plot( line_x, line_y_ransac, color="cornflowerblue", linewidth=lw, label=lbl, ) if optlist.ylimits: ax.set_ylim(optlist.ylimits[0], optlist.ylimits[1]) else: ax.set_ylim(sylim_lower, sylim_upper) ax.xaxis.set_tick_params(labelsize="x-small") ax.xaxis.set_major_formatter(ticker.EngFormatter()) ax.yaxis.set_tick_params(labelsize="x-small") ax.set_title( f"SRC:RSP {hdu_s.header['EXTNAME']}:{hdu_r.header['EXTNAME']}", fontsize="xx-small", ) handles, labels = ax.get_legend_handles_labels() lgnd = pu.mk_legend("inside", nprows, handles, labels, ax) # big hack print(f"sizes={lgnd.legendHandles[-2]._sizes}") lgnd.legendHandles[-2]._sizes = [6] lgnd.legendHandles[-1]._sizes = [6] if optlist.plot: for gidx in range(rindex + 1, nprows * npcols): # ax = np.ravel(axes)[int(gidx / npcols) * npcols + gidx % npcols] ax = np.ravel(axes)[gidx] ax.grid(False) ax.set_frame_on(False) ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) if srcid in rspids: ax = np.ravel(axes)[sindex] ax.grid(False) ax.set_frame_on(False) ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) fig.set_tight_layout({ "h_pad": 0.50, "w_pad": 1.0, "rect": [0, 0, 1, 0.97] }) plt.show() # end of doing stuff ncalls.counter = 0 # reset per file, triggers headers ncalls() # track call count, acts like static variable)
def imarith(): """main logic:""" optlist = parse_args() mu.init_logging(optlist.debug) mu.init_warnings() # evaluate operands as either a filename, float, floats or error verify_args(optlist) region = None if optlist.region: region = iu.parse_region(optlist.region) # Open files, throws exception on error hdulist1 = fits.open(optlist.operand1, mode="readonly") if os.path.isfile(optlist.operand2): hdulist2 = fits.open(optlist.operand2, mode="readonly") else: hdulist2 = None operand2 = optlist.operand2.split() # list of floats as strings # create output image with primary header and updates hdulisto = iu.create_output_hdulist(hdulist1, sys.argv) # loop over HDU id's from Master file, copy non-image HDUs # and process the image HDUs accordingly hduids = iu.get_requested_image_hduids(hdulist1, optlist.hduname, optlist.hduindex) if hduids is None: logging.info("No data HDUs found or requested") sys.exit(1) for hduid in hduids: # process these images # # This needs work to allow more flexible hdulist2 type images # as-is, it enforces that names match for corresponding hdu's hdu1 = hdulist1[hduid] if hdulist2: if isinstance(hdulist2[hduid], (fits.ImageHDU, fits.CompImageHDU)): hdu2 = hdulist2[hduid] else: logging.error("HDU %d does not exist in %s", hduid, hdulist2.filename()) # if hdulist2 and np.shape(hdu1.data) != np.shape(hdu2.data): logging.error("Images are not comensurate") sys.exit(1) # prepare the output hdu hduo = iu.init_image_hdu(hdu1, hdulisto, region) # optionally subtract bias if not optlist.sbias and not optlist.pbias: pass else: iu.subtract_bias(optlist.sbias, optlist.pbias, hdu1) if hdulist2: iu.subtract_bias(optlist.sbias, optlist.pbias, hdu2) # # do the arithmetic if hdulist2: hduo.data = ffcalc(hdu1.data, hdu2.data, optlist.op, region) else: # scalar or list of scalars if len(operand2) == 1: arg2 = float(operand2[0]) else: arg2 = float(operand2.pop(0)) hduo.data = fscalc(hdulist1[hduid].data, arg2, optlist.op, region) # finish up this hdu hduo.update_header() dtstr = datetime.datetime.utcnow().isoformat(timespec="milliseconds") hduo.add_checksum(dtstr) for hdu in hdulist1: # append to output if it does not contain image data if not isinstance(hdu, (fits.ImageHDU, fits.CompImageHDU, fits.PrimaryHDU)): hdulisto.append(hdu) # write the output file hdulisto.info() hdulisto.writeto(optlist.result, overwrite=True) sys.exit(0)
def imfft(): """main logic:""" optlist = parse_args() mu.init_logging(optlist.debug) mu.init_warnings() if optlist.scaling == "density": window = "boxcar" else: window = "flattop" # Open files fileno = 0 hduids = [] # loop over files for ffile in optlist.fitsfile: try: hdulist = fits.open(ffile) except IOError as ioerr: emsg = "IOError: {}".format(ioerr) logging.error(emsg) sys.exit(1) if optlist.info: # just print the image info and exit hdulist.info() continue # Construct a list of the HDU's to work on hduids = iu.get_requested_image_hduids(hdulist, optlist.hduname, optlist.hduindex) # loop over hdu's hducnt = 0 for hduid in hduids: hdr = hdulist[hduid].header try: dstr = hdr["DATASEC"] except KeyError as ke: emsg = "KeyError: {}, required".format(ke) logging.error(emsg) sys.exit(1) debugmsg = "DATASEC={}".format(dstr) logging.debug(debugmsg) res = re.match(r"\[*([0-9]*):([0-9]+),([0-9]+):([0-9]+)\]*", dstr) if res: datasec = res.groups() else: emsg = "DATASEC:{} parsing failed".format(dstr) logging.error(emsg) sys.exit(1) # define region to measure x1 = int(datasec[0]) - 1 x2 = int(datasec[1]) stddev = float(hdr["STDVBIAS"]) pix = hdulist[hduid].data fs = 1.0 / (optlist.rt * 1e-9) # measure the size needed arr = pix[optlist.row, x1:x2] x, p = signal.periodogram(arr, fs, window, scaling=optlist.scaling) flen = x.size plen = p.size if flen != plen: emsg = "flen({}) != plen({})".format(flen, plen) logging.error(emsg) emsg = "DATASEC:{} parsing failed".format(dstr) logging.error(emsg) sys.exit(1) # now do the real calculation f = np.empty((optlist.nrows, flen)) Pxx_den = np.empty((optlist.nrows, plen)) for rr in range(0, optlist.nrows): arr = pix[rr + optlist.row, x1:x2] if optlist.clip: amed = np.median(arr) farr = sigma_clip(arr) x, p = signal.periodogram(farr.filled(amed), fs, window, scaling=optlist.scaling) else: x, p = signal.periodogram(arr, fs, window, scaling=optlist.scaling) f[rr] = x Pxx_den[rr] = p f_avg = np.average(f, axis=0) Pxx_den_avg = np.average(Pxx_den, axis=0) # track the range needed for y-axis limits if (fileno + hducnt) == 0: pmin = Pxx_den_avg.min() pmax = Pxx_den_avg.max() debugmsg = "pmin0={:>g}".format(pmin) logging.debug(debugmsg) debugmsg = "pmax0={:>g}".format(pmax) logging.debug(debugmsg) else: if pmin > Pxx_den_avg.min(): pmin = Pxx_den_avg.min() debugmsg = "pmin={:>g}".format(pmin) logging.debug(debugmsg) if pmax < Pxx_den_avg.max(): pmax = Pxx_den_avg.max() debugmsg = "pmax={:>g}".format(pmax) logging.debug(debugmsg) plt.semilogy( f_avg, Pxx_den_avg, label="{}:{:>02d}:{:>7.2f}".format(fileno, hduid, stddev), ) hducnt += 1 # end loop over hdui's fileno += 1 # end loop over files # plt.ylim([0.8 * pmin, 1.2 * pmax]) plt.xlabel("freqquency [Hz]") if optlist.scaling == "density": plt.ylabel("PSD [V**2/Hz]") else: plt.ylabel("Linear spectrum [V RMS]") plt.grid(True) plt.legend(fontsize="xx-small", title="File:HDUi RN") plt.show()
def imcombine(): """main logic:""" optlist = parse_args() mu.init_logging(optlist.debug) mu.init_warnings() verbose = optlist.verbose # convert to slice format region = None if optlist.region: region = iu.parse_region(optlist.region) # Prepare scaling region in slice format scaling = None if optlist.scaling: scaling = iu.parse_region(optlist.scaling) # build file list if optlist.fitsfile: # input files listed on cmd line ifiles = optlist.fitsfile elif optlist.ifile: # input files listed in one or more files if not ifiles: ifiles = [] for b in mu.file_to_tokens(optlist.ifile): ifiles.extend(b) ifiles = sorted(list(set(ifiles))) # remove duplicates if verbose: print(f"using {len(ifiles)} input files") if optlist.debug: logging.debug("input files:") for ff in ifiles: logging.debug(" %s", ff) # get a list of verified images as hdulists # prepare input files for use (open and verify) if optlist.bimage: # include in verification ifiles.append(optlist.bimage) iimages = iu.files_to_hdulists(ifiles, True) if optlist.bimage: bimage = iimages.pop() # remove & assign last as bias image else: bimage = None # create output image with primary header and updates hdulisto = iu.create_output_hdulist(iimages[0], sys.argv) # get all requested hduids hduids_to_proc = iu.get_requested_hduids(iimages[0], optlist.hduname, optlist.hduindex) if hduids_to_proc is None: logging.error("No HDUs found or requested") sys.exit(1) # get just requested hduids with image data to be combined hduids_to_comb = iu.get_requested_image_hduids(iimages[0], optlist.hduname, optlist.hduindex) if hduids_to_comb is None: logging.error("No data HDUs found or requested") sys.exit(1) # choose the method to combine images if optlist.median: method = ["median"] if len(iimages) < 3: logging.warning("image count %d < 3, can only choose mean", len(iimages)) sys.exit() elif optlist.mean: method = ["mean"] elif optlist.sigmaclipped: method = ["sigmaclipped", optlist.sigmaclipped] if len(iimages) < 5: logging.warning("image count %d < 5, can only choose mean", len(iimages)) sys.exit() elif optlist.rank: method = ["rank", optlist.rank] if len(iimages) < 5: logging.warning("image count %d < 5, can only choose median, mean", len(iimages)) sys.exit() else: method = ["median"] # default # prepare the output image hdus using the first image as a template for hduid, hdui in enumerate(iimages[0]): # hdu image has data to combine if hduid in hduids_to_comb: logging.debug(f"processing hdu index {hduid}") hduo = iu.init_image_hdu(hdui, hdulisto, region) # this is the main algorithm/function iu.image_combine_hdu( iimages, hduid, method, region, bimage, optlist.sbias, optlist.ptype, scaling, hduo, ) # finish up this hdu hduo.update_header() dtstr = datetime.datetime.utcnow().isoformat( timespec="milliseconds") hduo.add_checksum(dtstr) # just append if image hdu has no data (eg. tables etc.) if hduid in hduids_to_proc: if not isinstance( hdui, (fits.ImageHDU, fits.CompImageHDU, fits.PrimaryHDU)): # append extensions that contain non-image data logging.debug(f"appending hdu index {hduid}") hdulisto.append(hdui) # write the output file hdulisto.writeto(optlist.result[0], overwrite=True)
def implot(): """main logic:""" optlist = parse_args() mu.init_logging(optlist.debug) mu.init_warnings() logging.debug("optlist: %s", optlist) # update/override some critical parameters plt.style.use(optlist.style) pu.update_rcparams() # uncomment to use latex # plt.rcParams["text.usetex"] = True # plt.rcParams["font.size"] = 12 # plt.rc("text.latex", preamble=r"\usepackage{underscore}") fig, axes = pu.get_fig_and_axis( len(optlist.fitsfile), optlist.layout, optlist.overlay, optlist.sharex, optlist.sharey, optlist.dpi, ) fsize = fig.get_size_inches() logging.debug("width= %5.2f, height= %5.2f", fsize[0], fsize[1]) logging.debug("len(axes)=%d", len(axes)) logging.debug("axes.shape= %s", axes.shape) nprows, npcols = (axes.shape[0], axes.shape[1]) logging.debug("nprows= %d, npcols= %d", nprows, npcols) pu.set_fig_title(optlist.title, optlist.fitsfile, fig) nfiles = len(optlist.fitsfile) # findex = 0 # for ffile in optlist.fitsfile: for findex in range(0, nfiles): try: hdulist = fits.open(optlist.fitsfile[findex], memmap=optlist.nomemmap) except IOError as ioerr: logging.error("IOError: %s", ioerr) sys.exit(1) # info option if optlist.info: hdulist.info() continue if optlist.overlay: ax = axes[0, 0] else: logging.debug( "ax = np.ravel(axes)[%d + %d]", int(findex / npcols) * npcols, findex % npcols, ) ax = np.ravel(axes)[int(findex / npcols) * npcols + findex % npcols] # construct a list of the HDU's to work on hduids = iu.get_requested_image_hduids(hdulist, optlist.hduname, optlist.hduindex) if hduids is None: logging.error("No valid HDUs found in %s", optlist.hduname or optlist.hduindex) sys.exit(1) # plot title is truncated filename (w/out path or .fit(s)) title_str = re.sub(r"^.*/(.*)$", r"\1", optlist.fitsfile[findex]) title_str = re.sub(r"^(.*)\.fits?(\.fz)*$", r"\1", title_str) if npcols < 3: title_nchars = 44 title_fontsize = "x-small" else: title_nchars = 32 title_fontsize = "xx-small" if len(title_str) > title_nchars: title_str = "{}...".format(title_str[:title_nchars]) else: title_str = "{}".format(title_str) logging.debug("using title_nchars=%d title_fontsize=%s", title_nchars, title_fontsize) if optlist.overlay: if nfiles > 1: # trunc'd filename in legend ax.plot([], [], " ", label=title_str) elif nfiles == 1 and not optlist.title: ax.set_title(title_str, fontsize=title_fontsize) if optlist.style == "ggplot": ax.plot([], []) # skip the first color else: ax.set_title(title_str, fontsize=title_fontsize) if optlist.style == "ggplot": ax.plot([], []) # skip the first color if optlist.xlimits: # for ax in np.ravel(axes): xbot, xtop = ax.set_xlim(optlist.xlimits[0], optlist.xlimits[1]) logging.debug("xbot= %.3g xtop= %.3g", xbot, xtop) if optlist.ylimits: # for ax in np.ravel(axes): ybot, ytop = ax.set_ylim(optlist.ylimits[0], optlist.ylimits[1]) logging.debug("ybot= %.3g ytop= %.3g", ybot, ytop) if optlist.logy: logging.debug("set_yscale(symlog)") ax.set_yscale("symlog") # y label depends on offset type if not optlist.offset: ax.set_ylabel("signal", size="x-small") elif optlist.offset == "mean": ax.set_ylabel("signal - mean", size="x-small") elif optlist.offset == "median": ax.set_ylabel("signal - median", size="x-small") elif optlist.offset == "delta": ax.set_ylabel("signal - mean + 5*j*stdev, j=0,1,..", size="x-small") else: logging.error("invalid --offset choice") sys.exit(1) # x label ax.grid(True) if optlist.row is not None: ax.set_xlabel("column", size="x-small") if optlist.ltype == "series": ax.set_xlabel("col series", size="x-small") elif optlist.col is not None: ax.set_xlabel("row", size="x-small") if optlist.ltype == "series": ax.set_xlabel("row series", size="x-small") else: logging.error("must have one of --row or --col") sys.exit(1) # do the plotting pu.plot_hdus(vars(optlist), hduids, hdulist, ax) # done with file, close it hdulist.close() # end of loop over files if optlist.info: # just print the image info and exit sys.exit() if optlist.title: # set the suptitle fig.set_tight_layout({ "h_pad": 0.50, "w_pad": 1.0, "rect": [0, 0, 1, 0.97] }) else: fig.set_tight_layout({ "h_pad": 0.50, "w_pad": 1.0, "rect": [0, 0, 1, 1] }) # Deal with the legend (ugly) # Get list of handles, labels from first plot ax = np.ravel(axes)[0] handles, labels = ax.get_legend_handles_labels() if nfiles == 1 or optlist.overlay: ax = np.ravel(axes)[0] else: ax = np.ravel(axes)[-1] # put legend in last slot pu.mk_legend(optlist.placement, nprows, handles, labels, ax) if not optlist.overlay: for gidx in range(nfiles, nprows * npcols): ax = np.ravel(axes)[int(gidx / npcols) * npcols + gidx % npcols] ax.grid(False) ax.set_frame_on(False) ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) if optlist.saveplot: fig.savefig(f"{optlist.saveplot}", dpi=600) plt.show()