def test_science(self): set_matplotlib_pdf_backend() import matplotlib.pyplot as plt test_dir = test_subdir_create("qa_test_science") log_file = os.path.join(test_dir, "log.txt") np.random.seed(123456789) input_mtl = os.path.join(test_dir, "mtl.fits") # For this test, we will use just 2 science target classes, in order to verify # we get approximately the correct distribution sdist = [(3000, 1, 0.25, "QSO"), (2000, 1, 0.75, "ELG")] nscience = sim_targets(input_mtl, TARGET_TYPE_SCIENCE, 0, density=self.density_science, science_frac=sdist) log_msg = "Simulated {} science targets\n".format(nscience) tgs = Targets() load_target_file(tgs, input_mtl) # Read hardware properties fp, exclude, state = sim_focalplane(rundate=test_assign_date) hw = load_hardware(focalplane=(fp, exclude, state)) tfile = os.path.join(test_dir, "footprint.fits") sim_tiles(tfile) tiles = load_tiles(tiles_file=tfile) # Precompute target positions tile_targetids, tile_x, tile_y = targets_in_tiles(hw, tgs, tiles) # Compute the targets available to each fiber for each tile. tgsavail = TargetsAvailable(hw, tiles, tile_targetids, tile_x, tile_y) # Compute the fibers on all tiles available for each target favail = LocationsAvailable(tgsavail) # Pass empty map of STUCK positioners that land on good sky stucksky = {} # Create assignment object asgn = Assignment(tgs, tgsavail, favail, stucksky) # First-pass assignment of science targets asgn.assign_unused(TARGET_TYPE_SCIENCE) # Redistribute asgn.redistribute_science() write_assignment_fits(tiles, asgn, out_dir=test_dir, all_targets=True) tile_ids = list(tiles.id) merge_results([input_mtl], list(), tile_ids, result_dir=test_dir, copy_fba=False) # FIXME: In order to use the qa_targets function, we need to know the # starting requested number of observations (NUMOBS_INIT). Then we can use # that value for each target and compare to the number actually assigned. # However, the NUMOBS_INIT column was removed from the merged TARGET table. # If we are ever able to reach consensus on restoring that column, then we # can re-enable these tests below. # # qa_targets( # hw, # tiles, # result_dir=test_dir, # result_prefix="fiberassign-" # ) # # # Load the target catalog so that we have access to the target properties # # fd = fitsio.FITS(input_mtl, "r") # scidata = np.array(np.sort(fd[1].read(), order="TARGETID")) # fd.close() # del fd # # # How many possible positioner assignments did we have? # nassign = 5000 * len(tile_ids) # # possible = dict() # achieved = dict() # # namepat = re.compile(r".*/qa_target_count_(.*)_init-(.*)\.fits") # for qafile in glob.glob("{}/qa_target_count_*.fits".format(test_dir)): # namemat = namepat.match(qafile) # name = namemat.group(1) # obs = int(namemat.group(2)) # if obs == 0: # continue # fd = fitsio.FITS(qafile, "r") # fdata = fd["COUNTS"].read() # # Sort by target ID so we can select easily # fdata = np.sort(fdata, order="TARGETID") # tgid = np.array(fdata["TARGETID"]) # counts = np.array(fdata["NUMOBS_DONE"]) # avail = np.array(fdata["NUMOBS_AVAIL"]) # del fdata # fd.close() # # # Select target properties. BOTH TARGET LISTS MUST BE SORTED. # rows = np.where(np.isin(scidata["TARGETID"], tgid, assume_unique=True))[0] # # ra = np.array(scidata["RA"][rows]) # dec = np.array(scidata["DEC"][rows]) # dtarget = np.array(scidata["DESI_TARGET"][rows]) # init = np.array(scidata["NUMOBS_INIT"][rows]) # # requested = obs * np.ones_like(avail) # # under = np.where(avail < requested)[0] # over = np.where(avail > requested)[0] # # limavail = np.array(avail) # limavail[over] = obs # # deficit = np.zeros(len(limavail), dtype=np.int) # # deficit[:] = limavail - counts # deficit[avail == 0] = 0 # # possible[name] = np.sum(limavail) # achieved[name] = np.sum(counts) # # log_msg += "{}-{}:\n".format(name, obs) # # pindx = np.where(deficit > 0)[0] # poor_tgid = tgid[pindx] # poor_dtarget = dtarget[pindx] # log_msg += " Deficit > 0: {}\n".format(len(poor_tgid)) # poor_ra = ra[pindx] # poor_dec = dec[pindx] # poor_deficit = deficit[pindx] # # # Plot Target availability # # Commented out by default, since in the case of high target density # # needed for maximizing assignments, there are far more targets than # # the number of available fiber placements. # # # marksize = 4 * np.ones_like(deficit) # # # # fig = plt.figure(figsize=(12, 12)) # # ax = fig.add_subplot(1, 1, 1) # # ax.scatter(ra, dec, s=2, c="black", marker="o") # # for pt, pr, pd, pdef in zip(poor_tgid, poor_ra, poor_dec, poor_deficit): # # ploc = plt.Circle( # # (pr, pd), radius=(0.05*pdef), fc="none", ec="red" # # ) # # ax.add_artist(ploc) # # ax.set_xlabel("RA", fontsize="large") # # ax.set_ylabel("DEC", fontsize="large") # # ax.set_title( # # "Target \"{}\": (min(avail, requested) - counts) > 0".format( # # name, obs # # ) # # ) # # #ax.legend(handles=lg, framealpha=1.0, loc="upper right") # # plt.savefig(os.path.join(test_dir, "{}-{}_deficit.pdf".format(name, obs)), dpi=300, format="pdf") # # log_msg += \ # "Assigned {} tiles for total of {} possible target observations\n".format( # len(tile_ids), nassign # ) # ach = 0 # for nm in possible.keys(): # ach += achieved[nm] # log_msg += \ # " type {} had {} possible target obs and achieved {}\n".format( # nm, possible[nm], achieved[nm] # ) # frac = 100.0 * ach / nassign # log_msg += \ # " {} / {} = {:0.2f}% of fibers were assigned\n".format( # ach, nassign, frac # ) # for nm in possible.keys(): # log_msg += \ # " type {} had {:0.2f}% of achieved observations\n".format( # nm, achieved[nm] / ach # ) # with open(log_file, "w") as f: # f.write(log_msg) # # self.assertGreaterEqual(frac, 99.0) # Test if qa-fiberassign script runs without crashing script = os.path.join(self.binDir, "qa-fiberassign") if os.path.exists(script): fafiles = glob.glob(f"{test_dir}/fiberassign-*.fits") cmd = "{} --targets {}".format(script, " ".join(fafiles)) err = subprocess.call(cmd.split()) self.assertEqual(err, 0, f"FAILED ({err}): {cmd}") else: print(f"ERROR: didn't find {script}")
def test_full(self): test_dir = test_subdir_create("assign_test_full") np.random.seed(123456789) input_mtl = os.path.join(test_dir, "mtl.fits") input_std = os.path.join(test_dir, "standards.fits") input_sky = os.path.join(test_dir, "sky.fits") nscience = sim_targets(input_mtl, TARGET_TYPE_SCIENCE, 0) nstd = sim_targets(input_std, TARGET_TYPE_STANDARD, nscience) nsky = sim_targets(input_sky, TARGET_TYPE_SKY, (nscience + nstd)) tgs = Targets() load_target_file(tgs, input_mtl) load_target_file(tgs, input_std) load_target_file(tgs, input_sky) # Create a hierarchical triangle mesh lookup of the targets positions tree = TargetTree(tgs, 0.01) # Read hardware properties fstatus = os.path.join(test_dir, "fiberstatus.ecsv") sim_status(fstatus) hw = load_hardware(status_file=fstatus) tfile = os.path.join(test_dir, "footprint.fits") sim_tiles(tfile) tiles = load_tiles(tiles_file=tfile) # Compute the targets available to each fiber for each tile. tgsavail = TargetsAvailable(hw, tgs, tiles, tree) # Free the tree del tree # Compute the fibers on all tiles available for each target favail = FibersAvailable(tgsavail) # Create assignment object asgn = Assignment(tgs, tgsavail, favail) # First-pass assignment of science targets asgn.assign_unused(TARGET_TYPE_SCIENCE) # Redistribute science targets asgn.redistribute_science() # Assign standards, 10 per petal asgn.assign_unused(TARGET_TYPE_STANDARD, 10) asgn.assign_force(TARGET_TYPE_STANDARD, 10) # Assign sky to unused fibers, up to 40 per petal asgn.assign_unused(TARGET_TYPE_SKY, 40) asgn.assign_force(TARGET_TYPE_SKY, 40) # If there are any unassigned fibers, try to place them somewhere. asgn.assign_unused(TARGET_TYPE_SCIENCE) asgn.assign_unused(TARGET_TYPE_SKY) write_assignment_fits(tiles, asgn, out_dir=test_dir, all_targets=True) plot_tiles(hw, tiles, result_dir=test_dir, plot_dir=test_dir, petals=[0], serial=True) qa_tiles(hw, tiles, result_dir=test_dir) qadata = None with open(os.path.join(test_dir, "qa.json"), "r") as f: qadata = json.load(f) for tile, props in qadata.items(): self.assertEqual(4495, props["assign_science"]) self.assertEqual(100, props["assign_std"]) self.assertEqual(400, props["assign_sky"]) plot_qa(qadata, os.path.join(test_dir, "qa"), outformat="pdf", labels=True) return
def test_full(self): test_dir = test_subdir_create("assign_test_full") np.random.seed(123456789) input_mtl = os.path.join(test_dir, "mtl.fits") input_std = os.path.join(test_dir, "standards.fits") input_sky = os.path.join(test_dir, "sky.fits") input_suppsky = os.path.join(test_dir, "suppsky.fits") tgoff = 0 nscience = sim_targets(input_mtl, TARGET_TYPE_SCIENCE, tgoff, density=self.density_science) tgoff += nscience nstd = sim_targets(input_std, TARGET_TYPE_STANDARD, tgoff, density=self.density_standards) tgoff += nstd nsky = sim_targets(input_sky, TARGET_TYPE_SKY, tgoff, density=self.density_sky) tgoff += nsky nsuppsky = sim_targets(input_suppsky, TARGET_TYPE_SUPPSKY, tgoff, density=self.density_suppsky) tgs = Targets() load_target_file(tgs, input_mtl) load_target_file(tgs, input_std) load_target_file(tgs, input_sky) load_target_file(tgs, input_suppsky) # Create a hierarchical triangle mesh lookup of the targets positions tree = TargetTree(tgs, 0.01) # Read hardware properties fp, exclude, state = sim_focalplane(rundate=test_assign_date) hw = load_hardware(focalplane=(fp, exclude, state)) tfile = os.path.join(test_dir, "footprint.fits") sim_tiles(tfile) tiles = load_tiles(tiles_file=tfile) # Compute the targets available to each fiber for each tile. tgsavail = TargetsAvailable(hw, tgs, tiles, tree) # Free the tree del tree # Compute the fibers on all tiles available for each target favail = LocationsAvailable(tgsavail) # Create assignment object asgn = Assignment(tgs, tgsavail, favail) # First-pass assignment of science targets asgn.assign_unused(TARGET_TYPE_SCIENCE) # Redistribute science targets asgn.redistribute_science() # Assign standards, 10 per petal asgn.assign_unused(TARGET_TYPE_STANDARD, 10) asgn.assign_force(TARGET_TYPE_STANDARD, 10) # Assign sky to unused fibers, up to 40 per petal asgn.assign_unused(TARGET_TYPE_SKY, 40) asgn.assign_force(TARGET_TYPE_SKY, 40) # Use supplemental sky to meet our requirements asgn.assign_unused(TARGET_TYPE_SUPPSKY, 40) asgn.assign_force(TARGET_TYPE_SUPPSKY, 40) # If there are any unassigned fibers, try to place them somewhere. asgn.assign_unused(TARGET_TYPE_SCIENCE) asgn.assign_unused(TARGET_TYPE_SKY) asgn.assign_unused(TARGET_TYPE_SUPPSKY) write_assignment_fits(tiles, asgn, out_dir=test_dir, all_targets=True) plotpetals = [0] #plotpetals = None plot_tiles(hw, tiles, result_dir=test_dir, plot_dir=test_dir, result_prefix="fba-", real_shapes=True, petals=plotpetals, serial=True) qa_tiles(hw, tiles, result_dir=test_dir) qadata = None with open(os.path.join(test_dir, "qa.json"), "r") as f: qadata = json.load(f) for tile, props in qadata.items(): self.assertTrue(props["assign_science"] >= 4485) self.assertEqual(100, props["assign_std"]) self.assertTrue( (props["assign_sky"] + props["assign_suppsky"]) >= 400) plot_qa(qadata, os.path.join(test_dir, "qa"), outformat="pdf", labels=True) return
def test_science(self): set_matplotlib_pdf_backend() import matplotlib.pyplot as plt test_dir = test_subdir_create("qa_test_science") log_file = os.path.join(test_dir, "log.txt") np.random.seed(123456789) input_mtl = os.path.join(test_dir, "mtl.fits") # For this test, we will use just 2 science target classes, in order to verify # we get approximately the correct distribution sdist = [(3000, 1, 0.25, "QSO"), (2000, 1, 0.75, "ELG")] nscience = sim_targets(input_mtl, TARGET_TYPE_SCIENCE, 0, density=self.density_science, science_frac=sdist) log_msg = "Simulated {} science targets\n".format(nscience) tgs = Targets() load_target_file(tgs, input_mtl) # Create a hierarchical triangle mesh lookup of the targets positions tree = TargetTree(tgs, 0.01) # Read hardware properties fp, exclude, state = sim_focalplane(rundate=test_assign_date) hw = load_hardware(focalplane=(fp, exclude, state)) tfile = os.path.join(test_dir, "footprint.fits") sim_tiles(tfile) tiles = load_tiles(tiles_file=tfile) # Compute the targets available to each fiber for each tile. tgsavail = TargetsAvailable(hw, tgs, tiles, tree) # Free the tree del tree # Compute the fibers on all tiles available for each target favail = LocationsAvailable(tgsavail) # Create assignment object asgn = Assignment(tgs, tgsavail, favail) # First-pass assignment of science targets asgn.assign_unused(TARGET_TYPE_SCIENCE) # Redistribute asgn.redistribute_science() write_assignment_fits(tiles, asgn, out_dir=test_dir, all_targets=True) tile_ids = list(tiles.id) merge_results([input_mtl], list(), tile_ids, result_dir=test_dir, copy_fba=False) # if "TRAVIS" not in os.environ: # plot_tiles( # hw, # tiles, # result_dir=test_dir, # plot_dir=test_dir, # real_shapes=True, # serial=True # ) qa_targets(hw, tiles, result_dir=test_dir, result_prefix="fiberassign-") # Load the target catalog so that we have access to the target properties fd = fitsio.FITS(input_mtl, "r") scidata = np.array(np.sort(fd[1].read(), order="TARGETID")) fd.close() del fd # How many possible positioner assignments did we have? nassign = 5000 * len(tile_ids) possible = dict() achieved = dict() namepat = re.compile(r".*/qa_target_count_(.*)_init-(.*)\.fits") for qafile in glob.glob("{}/qa_target_count_*.fits".format(test_dir)): namemat = namepat.match(qafile) name = namemat.group(1) obs = int(namemat.group(2)) if obs == 0: continue fd = fitsio.FITS(qafile, "r") fdata = fd["COUNTS"].read() # Sort by target ID so we can select easily fdata = np.sort(fdata, order="TARGETID") tgid = np.array(fdata["TARGETID"]) counts = np.array(fdata["NUMOBS_DONE"]) avail = np.array(fdata["NUMOBS_AVAIL"]) del fdata fd.close() # Select target properties. BOTH TARGET LISTS MUST BE SORTED. rows = np.where( np.isin(scidata["TARGETID"], tgid, assume_unique=True))[0] ra = np.array(scidata["RA"][rows]) dec = np.array(scidata["DEC"][rows]) dtarget = np.array(scidata["DESI_TARGET"][rows]) init = np.array(scidata["NUMOBS_MORE"][rows]) requested = obs * np.ones_like(avail) under = np.where(avail < requested)[0] over = np.where(avail > requested)[0] limavail = np.array(avail) limavail[over] = obs deficit = np.zeros(len(limavail), dtype=np.int) deficit[:] = limavail - counts deficit[avail == 0] = 0 possible[name] = np.sum(limavail) achieved[name] = np.sum(counts) log_msg += "{}-{}:\n".format(name, obs) pindx = np.where(deficit > 0)[0] poor_tgid = tgid[pindx] poor_dtarget = dtarget[pindx] log_msg += " Deficit > 0: {}\n".format(len(poor_tgid)) poor_ra = ra[pindx] poor_dec = dec[pindx] poor_deficit = deficit[pindx] # Plot Target availability # Commented out by default, since in the case of high target density # needed for maximizing assignments, there are far more targets than # the number of available fiber placements. # marksize = 4 * np.ones_like(deficit) # # fig = plt.figure(figsize=(12, 12)) # ax = fig.add_subplot(1, 1, 1) # ax.scatter(ra, dec, s=2, c="black", marker="o") # for pt, pr, pd, pdef in zip(poor_tgid, poor_ra, poor_dec, poor_deficit): # ploc = plt.Circle( # (pr, pd), radius=(0.05*pdef), fc="none", ec="red" # ) # ax.add_artist(ploc) # ax.set_xlabel("RA", fontsize="large") # ax.set_ylabel("DEC", fontsize="large") # ax.set_title( # "Target \"{}\": (min(avail, requested) - counts) > 0".format( # name, obs # ) # ) # #ax.legend(handles=lg, framealpha=1.0, loc="upper right") # plt.savefig(os.path.join(test_dir, "{}-{}_deficit.pdf".format(name, obs)), dpi=300, format="pdf") log_msg += \ "Assigned {} tiles for total of {} possible target observations\n".format( len(tile_ids), nassign ) ach = 0 for nm in possible.keys(): ach += achieved[nm] log_msg += \ " type {} had {} possible target obs and achieved {}\n".format( nm, possible[nm], achieved[nm] ) frac = 100.0 * ach / nassign log_msg += \ " {} / {} = {:0.2f}% of fibers were assigned\n".format( ach, nassign, frac ) for nm in possible.keys(): log_msg += \ " type {} had {:0.2f}% of achieved observations\n".format( nm, achieved[nm] / ach ) with open(log_file, "w") as f: f.write(log_msg) self.assertGreaterEqual(frac, 99.0) #- Test if qa-fiberassign script runs without crashing bindir = os.path.join(os.path.dirname(fiberassign.__file__), '..', '..', 'bin') script = os.path.join(os.path.abspath(bindir), 'qa-fiberassign') if os.path.exists(script): fafiles = glob.glob(f"{test_dir}/fiberassign-*.fits") cmd = "{} --targets {}".format(script, " ".join(fafiles)) err = subprocess.call(cmd.split()) self.assertEqual(err, 0, f"FAILED ({err}): {cmd}") else: print(f"ERROR: didn't find {script}")
favail = FibersAvailable(tgsavail) # Create assignment object asgn = Assignment(tgs, tgsavail, favail) # -------------------------------------------------------------------------------------------------- # FIBER ASSIGNMENT PROCESS # # Fiber assignment process is carried out here. Results are stored in the Assignment type object. # -------------------------------------------------------------------------------------------------- # First-pass assignment of science targets asgn.assign_unused(TARGET_TYPE_SCIENCE) # Redistribute science targets across available petals asgn.redistribute_science() # Assign standards, 10 per petal asgn.assign_unused(TARGET_TYPE_STANDARD, 10) asgn.assign_force(TARGET_TYPE_STANDARD, 10) # Assign sky, up to 40 per petal asgn.assign_unused(TARGET_TYPE_SKY, 40) asgn.assign_force(TARGET_TYPE_SKY, 40) # If there are any unassigned fibers, try to place them somewhere. asgn.assign_unused(TARGET_TYPE_SCIENCE) asgn.assign_unused(TARGET_TYPE_SKY) # -------------------------------------------------------------------------------------------------- # EXTRACTING THE ASSIGNED TARGET IDS