LSST Applications g0265f82a02+c6dfa2ddaf,g1162b98a3f+b2075782a9,g2079a07aa2+1b2e822518,g2bbee38e9b+c6dfa2ddaf,g337abbeb29+c6dfa2ddaf,g3ddfee87b4+a60788ef87,g50ff169b8f+2eb0e556e8,g52b1c1532d+90ebb246c7,g555ede804d+a60788ef87,g591dd9f2cf+ba8caea58f,g5ec818987f+864ee9cddb,g858d7b2824+9ee1ab4172,g876c692160+a40945ebb7,g8a8a8dda67+90ebb246c7,g8cdfe0ae6a+4fd9e222a8,g99cad8db69+5e309b7bc6,g9ddcbc5298+a1346535a5,ga1e77700b3+df8f93165b,ga8c6da7877+aa12a14d27,gae46bcf261+c6dfa2ddaf,gb0e22166c9+8634eb87fb,gb3f2274832+d0da15e3be,gba4ed39666+1ac82b564f,gbb8dafda3b+5dfd9c994b,gbeb006f7da+97157f9740,gc28159a63d+c6dfa2ddaf,gc86a011abf+9ee1ab4172,gcf0d15dbbd+a60788ef87,gdaeeff99f8+1cafcb7cd4,gdc0c513512+9ee1ab4172,ge79ae78c31+c6dfa2ddaf,geb67518f79+ba1859f325,geb961e4c1e+f9439d1e6f,gee10cc3b42+90ebb246c7,gf1cff7945b+9ee1ab4172,w.2024.12
LSST Data Management Base Package
Loading...
Searching...
No Matches
Public Member Functions | Static Public Attributes | Protected Member Functions | Static Protected Attributes | List of all members
lsst.fgcmcal.fgcmBuildFromIsolatedStars.FgcmBuildFromIsolatedStarsTask Class Reference
Inheritance diagram for lsst.fgcmcal.fgcmBuildFromIsolatedStars.FgcmBuildFromIsolatedStarsTask:
lsst.fgcmcal.fgcmBuildStarsBase.FgcmBuildStarsBaseTask

Public Member Functions

 __init__ (self, initInputs=None, **kwargs)
 
 runQuantum (self, butlerQC, inputRefs, outputRefs)
 
 run (self, *camera, visit_summary_handle_dict, isolated_star_cat_handle_dict, isolated_star_source_handle_dict, lookup_table_handle=None)
 

Static Public Attributes

 ConfigClass = FgcmBuildFromIsolatedStarsConfig
 
bool canMultiprocess = False
 

Protected Member Functions

 _make_all_star_obs_from_isolated_stars (self, isolated_star_cat_handle_dict, isolated_star_source_handle_dict, visit_cat, camera, calib_flux_aperture_radius=None)
 
 _density_downsample (self, fgcm_obj, star_obs)
 
 _mark_reserve_stars (self, fgcm_obj)
 
 _associate_reference_stars (self, lookup_table_handle, visit_cat, fgcm_obj)
 
 _compute_delta_aper_summary_statistics (self, visit_cat, star_obs)
 

Static Protected Attributes

str _DefaultName = "fgcmBuildFromIsolatedStars"
 

Detailed Description

Build star catalog for FGCM global calibration, using the isolated star catalogs.

Definition at line 185 of file fgcmBuildFromIsolatedStars.py.

Constructor & Destructor Documentation

◆ __init__()

lsst.fgcmcal.fgcmBuildFromIsolatedStars.FgcmBuildFromIsolatedStarsTask.__init__ ( self,
initInputs = None,
** kwargs )

Reimplemented from lsst.fgcmcal.fgcmBuildStarsBase.FgcmBuildStarsBaseTask.

Definition at line 193 of file fgcmBuildFromIsolatedStars.py.

193 def __init__(self, initInputs=None, **kwargs):
194 super().__init__(**kwargs)
195 self.makeSubtask('reserve_selection')
196

Member Function Documentation

◆ _associate_reference_stars()

lsst.fgcmcal.fgcmBuildFromIsolatedStars.FgcmBuildFromIsolatedStarsTask._associate_reference_stars ( self,
lookup_table_handle,
visit_cat,
fgcm_obj )
protected
Associate fgcm isolated stars with reference stars.

Parameters
----------
lookup_table_handle : `lsst.daf.butler.DeferredDatasetHandle`, optional
    Data reference to fgcm look-up table (used if matching reference stars).
visit_cat : `lsst.afw.table.ExposureCatalog`
    Catalog of per-visit data.
fgcm_obj : `astropy.table.Table`
    Catalog of per-star ids and positions

Returns
-------
ref_cat : `astropy.table.Table`
    Catalog of reference stars matched to fgcm stars.

Definition at line 688 of file fgcmBuildFromIsolatedStars.py.

688 def _associate_reference_stars(self, lookup_table_handle, visit_cat, fgcm_obj):
689 """Associate fgcm isolated stars with reference stars.
690
691 Parameters
692 ----------
693 lookup_table_handle : `lsst.daf.butler.DeferredDatasetHandle`, optional
694 Data reference to fgcm look-up table (used if matching reference stars).
695 visit_cat : `lsst.afw.table.ExposureCatalog`
696 Catalog of per-visit data.
697 fgcm_obj : `astropy.table.Table`
698 Catalog of per-star ids and positions
699
700 Returns
701 -------
702 ref_cat : `astropy.table.Table`
703 Catalog of reference stars matched to fgcm stars.
704 """
705 # Figure out the correct bands/filters that we need based on the data.
706 lut_cat = lookup_table_handle.get()
707
708 std_filter_dict = {filter_name: std_filter for (filter_name, std_filter) in
709 zip(lut_cat[0]["physicalFilters"].split(","),
710 lut_cat[0]["stdPhysicalFilters"].split(","))}
711 std_lambda_dict = {std_filter: std_lambda for (std_filter, std_lambda) in
712 zip(lut_cat[0]["stdPhysicalFilters"].split(","),
713 lut_cat[0]["lambdaStdFilter"])}
714 del lut_cat
715
716 reference_filter_names = self._getReferenceFilterNames(
717 visit_cat,
718 std_filter_dict,
719 std_lambda_dict,
720 )
721 self.log.info("Using the following reference filters: %s", (", ".join(reference_filter_names)))
722
723 # Split into healpix pixels for more efficient matching.
724 ipnest = hpg.angle_to_pixel(self.config.coarseNside, fgcm_obj["ra"], fgcm_obj["dec"])
725 hpix, revpix = histogram_rev_sorted(ipnest)
726
727 pixel_cats = []
728
729 # Compute the dtype from the filter names.
730 dtype = [("fgcm_id", "i4"),
731 ("refMag", "f4", (len(reference_filter_names), )),
732 ("refMagErr", "f4", (len(reference_filter_names), ))]
733
734 (gdpix,) = (hpix > 0).nonzero()
735 for ii, gpix in enumerate(gdpix):
736 p1a = revpix[revpix[gpix]: revpix[gpix + 1]]
737
738 # We do a simple wrapping of RA if we need to.
739 ra_wrap = fgcm_obj["ra"][p1a]
740 if (ra_wrap.min() < 10.0) and (ra_wrap.max() > 350.0):
741 ra_wrap[ra_wrap > 180.0] -= 360.0
742 mean_ra = np.mean(ra_wrap) % 360.0
743 else:
744 mean_ra = np.mean(ra_wrap)
745 mean_dec = np.mean(fgcm_obj["dec"][p1a])
746
747 dist = esutil.coords.sphdist(mean_ra, mean_dec, fgcm_obj["ra"][p1a], fgcm_obj["dec"][p1a])
748 rad = dist.max()
749
750 if rad < hpg.nside_to_resolution(self.config.coarseNside)/2.:
751 # Small radius, read just the circle.
752 ref_cat = self.fgcmLoadReferenceCatalog.getFgcmReferenceStarsSkyCircle(
753 mean_ra,
754 mean_dec,
755 rad,
756 reference_filter_names,
757 )
758 else:
759 # Otherwise the less efficient but full coverage.
760 ref_cat = self.fgcmLoadReferenceCatalog.getFgcmReferenceStarsHealpix(
761 self.config.coarseNside,
762 hpg.nest_to_ring(self.config.coarseNside, ipnest[p1a[0]]),
763 reference_filter_names,
764 )
765 if ref_cat.size == 0:
766 # No stars in this pixel; that's okay.
767 continue
768
769 with Matcher(fgcm_obj["ra"][p1a], fgcm_obj["dec"][p1a]) as matcher:
770 idx, i1, i2, d = matcher.query_radius(
771 ref_cat["ra"],
772 ref_cat["dec"],
773 self.config.matchRadius/3600.,
774 return_indices=True,
775 )
776
777 if len(i1) == 0:
778 # No matched stars in this pixel; that's okay.
779 continue
780
781 pixel_cat = Table(data=np.zeros(i1.size, dtype=dtype))
782 pixel_cat["fgcm_id"] = fgcm_obj["fgcm_id"][p1a[i1]]
783 pixel_cat["refMag"][:, :] = ref_cat["refMag"][i2, :]
784 pixel_cat["refMagErr"][:, :] = ref_cat["refMagErr"][i2, :]
785
786 pixel_cats.append(pixel_cat)
787
788 self.log.info(
789 "Found %d reference matches in pixel %d (%d of %d).",
790 len(pixel_cat),
791 ipnest[p1a[0]],
792 ii,
793 gdpix.size - 1,
794 )
795
796 ref_cat_full = vstack(pixel_cats)
797 ref_cat_full.meta.update({'FILTERNAMES': reference_filter_names})
798
799 return ref_cat_full
800

◆ _compute_delta_aper_summary_statistics()

lsst.fgcmcal.fgcmBuildFromIsolatedStars.FgcmBuildFromIsolatedStarsTask._compute_delta_aper_summary_statistics ( self,
visit_cat,
star_obs )
protected
Compute delta aperture summary statistics (per visit).

Parameters
----------
visit_cat : `lsst.afw.table.ExposureCatalog`
    Catalog of per-visit data.
star_obs : `astropy.table.Table`
    Catalog of individual star observations.

Definition at line 801 of file fgcmBuildFromIsolatedStars.py.

801 def _compute_delta_aper_summary_statistics(self, visit_cat, star_obs):
802 """Compute delta aperture summary statistics (per visit).
803
804 Parameters
805 ----------
806 visit_cat : `lsst.afw.table.ExposureCatalog`
807 Catalog of per-visit data.
808 star_obs : `astropy.table.Table`
809 Catalog of individual star observations.
810 """
811 (ok,) = ((star_obs["delta_mag_aper"] < 99.0)
812 & (star_obs["delta_mag_err_aper"] < 99.0)).nonzero()
813
814 visit_index = np.zeros(len(star_obs[ok]), dtype=np.int32)
815 a, b = esutil.numpy_util.match(visit_cat["visit"], star_obs["visit"][ok])
816 visit_index[b] = a
817
818 h, rev = histogram_rev_sorted(visit_index)
819
820 visit_cat["deltaAper"] = -9999.0
821 h_use, = np.where(h >= 3)
822 for index in h_use:
823 i1a = rev[rev[index]: rev[index + 1]]
824 visit_cat["deltaAper"][index] = np.median(star_obs["delta_mag_aper"][ok[i1a]])

◆ _density_downsample()

lsst.fgcmcal.fgcmBuildFromIsolatedStars.FgcmBuildFromIsolatedStarsTask._density_downsample ( self,
fgcm_obj,
star_obs )
protected
Downsample stars according to density.

Catalogs are modified in-place.

Parameters
----------
fgcm_obj : `astropy.table.Table`
    Catalog of per-star ids and positions.
star_obs : `astropy.table.Table`
    Catalog of individual star observations.

Definition at line 622 of file fgcmBuildFromIsolatedStars.py.

622 def _density_downsample(self, fgcm_obj, star_obs):
623 """Downsample stars according to density.
624
625 Catalogs are modified in-place.
626
627 Parameters
628 ----------
629 fgcm_obj : `astropy.table.Table`
630 Catalog of per-star ids and positions.
631 star_obs : `astropy.table.Table`
632 Catalog of individual star observations.
633 """
634 if self.config.randomSeed is not None:
635 np.random.seed(seed=self.config.randomSeed)
636
637 ipnest = hpg.angle_to_pixel(self.config.densityCutNside, fgcm_obj["ra"], fgcm_obj["dec"])
638 # Use the esutil.stat.histogram function to pull out the histogram of
639 # grouped pixels, and the rev_indices which describes which inputs
640 # are grouped together. The fgcm histogram_rev_sorted shim
641 # ensures that the indices are properly sorted.
642 hist, rev_indices = histogram_rev_sorted(ipnest)
643
644 obj_use = np.ones(len(fgcm_obj), dtype=bool)
645
646 (high,) = (hist > self.config.densityCutMaxPerPixel).nonzero()
647 (ok,) = (hist > 0).nonzero()
648 self.log.info("There are %d/%d pixels with high stellar density.", high.size, ok.size)
649 for i in range(high.size):
650 # The pix_indices are the indices of every star in the pixel.
651 pix_indices = rev_indices[rev_indices[high[i]]: rev_indices[high[i] + 1]]
652 # Cut down to the maximum number of stars in the pixel.
653 cut = np.random.choice(
654 pix_indices,
655 size=pix_indices.size - self.config.densityCutMaxPerPixel,
656 replace=False,
657 )
658 obj_use[cut] = False
659
660 fgcm_obj = fgcm_obj[obj_use]
661
662 obs_index = np.zeros(np.sum(fgcm_obj["n_obs"]), dtype=np.int32)
663 ctr = 0
664 for i in range(len(fgcm_obj)):
665 n_obs = fgcm_obj["n_obs"][i]
666 obs_index[ctr: ctr + n_obs] = (
667 np.arange(fgcm_obj["obs_arr_index"][i], fgcm_obj["obs_arr_index"][i] + n_obs)
668 )
669 fgcm_obj["obs_arr_index"][i] = ctr
670 ctr += n_obs
671
672 star_obs = star_obs[obs_index]
673

◆ _make_all_star_obs_from_isolated_stars()

lsst.fgcmcal.fgcmBuildFromIsolatedStars.FgcmBuildFromIsolatedStarsTask._make_all_star_obs_from_isolated_stars ( self,
isolated_star_cat_handle_dict,
isolated_star_source_handle_dict,
visit_cat,
camera,
calib_flux_aperture_radius = None )
protected
Make all star observations from isolated star catalogs.

Parameters
----------
isolated_star_cat_handle_dict : `dict` [`int`, `lsst.daf.butler.DeferredDatasetHandle`]
    Isolated star catalog dataset handles, with the tract as key.
isolated_star_source_handle_dict : `dict` [`int`, `lsst.daf.butler.DeferredDatasetHandle`]
    Isolated star source dataset handles, with the tract as key.
visit_cat : `lsst.afw.table.ExposureCatalog`
    Catalog of per-visit data.
camera : `lsst.afw.cameraGeom.Camera`
    The camera object.
calib_flux_aperture_radius : `float`, optional
    Radius for the calibration flux aperture.

Returns
-------
fgcm_obj : `astropy.table.Table`
    Catalog of ids and positions for each star.
star_obs : `astropy.table.Table`
    Catalog of individual star observations.

Definition at line 340 of file fgcmBuildFromIsolatedStars.py.

347 ):
348 """Make all star observations from isolated star catalogs.
349
350 Parameters
351 ----------
352 isolated_star_cat_handle_dict : `dict` [`int`, `lsst.daf.butler.DeferredDatasetHandle`]
353 Isolated star catalog dataset handles, with the tract as key.
354 isolated_star_source_handle_dict : `dict` [`int`, `lsst.daf.butler.DeferredDatasetHandle`]
355 Isolated star source dataset handles, with the tract as key.
356 visit_cat : `lsst.afw.table.ExposureCatalog`
357 Catalog of per-visit data.
358 camera : `lsst.afw.cameraGeom.Camera`
359 The camera object.
360 calib_flux_aperture_radius : `float`, optional
361 Radius for the calibration flux aperture.
362
363 Returns
364 -------
365 fgcm_obj : `astropy.table.Table`
366 Catalog of ids and positions for each star.
367 star_obs : `astropy.table.Table`
368 Catalog of individual star observations.
369 """
370 source_columns = [
371 "sourceId",
372 "visit",
373 "detector",
374 "ra",
375 "dec",
376 "x",
377 "y",
378 "physical_filter",
379 "band",
380 "obj_index",
381 self.config.instFluxField,
382 self.config.instFluxField + "Err",
383 self.config.apertureInnerInstFluxField,
384 self.config.apertureInnerInstFluxField + "Err",
385 self.config.apertureOuterInstFluxField,
386 self.config.apertureOuterInstFluxField + "Err",
387 ]
388
389 if self.config.doSubtractLocalBackground:
390 source_columns.append(self.config.localBackgroundFluxField)
391 local_background_flag_name = self.config.localBackgroundFluxField[0: -len('instFlux')] + 'flag'
392 source_columns.append(local_background_flag_name)
393
394 if self.sourceSelector.config.doFlags:
395 source_columns.extend(self.sourceSelector.config.flags.bad)
396
397 local_background_area = np.pi*calib_flux_aperture_radius**2.
398
399 # Compute the approximate pixel area over the full focal plane
400 # from the WCS jacobian using the camera model.
401 approx_pixel_area_fields = computeApproxPixelAreaFields(camera)
402
403 # Construct mapping from detector number to index.
404 detector_mapping = {}
405 for detector_index, detector in enumerate(camera):
406 detector_mapping[detector.getId()] = detector_index
407
408 star_obs_dtype = [
409 ("ra", "f8"),
410 ("dec", "f8"),
411 ("x", "f8"),
412 ("y", "f8"),
413 ("visit", "i8"),
414 ("detector", "i4"),
415 ("inst_mag", "f4"),
416 ("inst_mag_err", "f4"),
417 ("jacobian", "f4"),
418 ("delta_mag_bkg", "f4"),
419 ("delta_mag_aper", "f4"),
420 ("delta_mag_err_aper", "f4"),
421 ]
422
423 fgcm_obj_dtype = [
424 ("fgcm_id", "i8"),
425 ("isolated_star_id", "i8"),
426 ("ra", "f8"),
427 ("dec", "f8"),
428 ("obs_arr_index", "i4"),
429 ("n_obs", "i4"),
430 ("obj_flag", "i4"),
431 ]
432
433 fgcm_objs = []
434 star_obs_cats = []
435 merge_source_counter = 0
436
437 k = 2.5/np.log(10.)
438
439 visit_cat_table = visit_cat.asAstropy()
440
441 for tract in sorted(isolated_star_cat_handle_dict):
442 stars = isolated_star_cat_handle_dict[tract].get()
443 sources = isolated_star_source_handle_dict[tract].get(parameters={"columns": source_columns})
444
445 # Down-select sources.
446 good_sources = self.sourceSelector.selectSources(sources).selected
447 if self.config.doSubtractLocalBackground:
448 good_sources &= (~sources[local_background_flag_name])
449 local_background = local_background_area*sources[self.config.localBackgroundFluxField]
450 good_sources &= ((sources[self.config.instFluxField] - local_background) > 0)
451
452 if good_sources.sum() == 0:
453 self.log.info("No good sources found in tract %d", tract)
454 continue
455
456 # Need to count the observations of each star after cuts, per band.
457 # If we have requiredBands specified, we must make sure that each star
458 # has the minumum number of observations in each of thos bands.
459 # Otherwise, we must make sure that each star has at least the minimum
460 # number of observations in _any_ band.
461 if len(self.config.requiredBands) > 0:
462 loop_bands = self.config.requiredBands
463 else:
464 loop_bands = np.unique(sources["band"])
465
466 n_req = np.zeros((len(loop_bands), len(stars)), dtype=np.int32)
467 for i, band in enumerate(loop_bands):
468 (band_use,) = (sources[good_sources]["band"] == band).nonzero()
469 np.add.at(
470 n_req,
471 (i, sources[good_sources]["obj_index"][band_use]),
472 1,
473 )
474
475 if len(self.config.requiredBands) > 0:
476 # The min gives us the band with the fewest observations, which must be
477 # above the limit.
478 (good_stars,) = (n_req.min(axis=0) >= self.config.minPerBand).nonzero()
479 else:
480 # The max gives us the band with the most observations, which must be
481 # above the limit.
482 (good_stars,) = (n_req.max(axis=0) >= self.config.minPerBand).nonzero()
483
484 if len(good_stars) == 0:
485 self.log.info("No good stars found in tract %d", tract)
486 continue
487
488 # With the following matching:
489 # sources[good_sources][b] <-> stars[good_stars[a]]
490 obj_index = sources["obj_index"][good_sources]
491 a, b = esutil.numpy_util.match(good_stars, obj_index)
492
493 # Update indexes and cut to selected stars/sources
494 _, index_new = np.unique(a, return_index=True)
495 stars["source_cat_index"][good_stars] = index_new
496 sources = sources[good_sources][b]
497 sources["obj_index"][:] = a
498 stars = stars[good_stars]
499
500 nsource = np.zeros(len(stars), dtype=np.int32)
501 np.add.at(
502 nsource,
503 sources["obj_index"],
504 1,
505 )
506 stars["nsource"][:] = nsource
507
508 # After these cuts, the catalogs have the following properties:
509 # - ``stars`` only contains isolated stars that have the minimum number of good
510 # sources in the required bands.
511 # - ``sources`` has been cut to the good sources.
512 # - The slice [stars["source_cat_index"]: stars["source_cat_index"]
513 # + stars["nsource"]]
514 # applied to ``sources`` will give all the sources associated with the star.
515 # - For each source, sources["obj_index"] points to the index of the associated
516 # isolated star.
517
518 # We now reformat the sources and compute the ``observations`` that fgcm expects.
519 star_obs = Table(data=np.zeros(len(sources), dtype=star_obs_dtype))
520 star_obs["ra"] = sources["ra"]
521 star_obs["dec"] = sources["dec"]
522 star_obs["x"] = sources["x"]
523 star_obs["y"] = sources["y"]
524 star_obs["visit"] = sources["visit"]
525 star_obs["detector"] = sources["detector"]
526
527 visit_match, obs_match = esutil.numpy_util.match(visit_cat_table["visit"], sources["visit"])
528
529 exp_time = np.zeros(len(star_obs))
530 exp_time[obs_match] = visit_cat_table["exptime"][visit_match]
531
532 with warnings.catch_warnings():
533 # Ignore warnings, we will filter infinities and nans below.
534 warnings.simplefilter("ignore")
535
536 inst_mag_inner = -2.5*np.log10(sources[self.config.apertureInnerInstFluxField])
537 inst_mag_err_inner = k*(sources[self.config.apertureInnerInstFluxField + "Err"]
538 / sources[self.config.apertureInnerInstFluxField])
539 inst_mag_outer = -2.5*np.log10(sources[self.config.apertureOuterInstFluxField])
540 inst_mag_err_outer = k*(sources[self.config.apertureOuterInstFluxField + "Err"]
541 / sources[self.config.apertureOuterInstFluxField])
542 star_obs["delta_mag_aper"] = inst_mag_inner - inst_mag_outer
543 star_obs["delta_mag_err_aper"] = np.sqrt(inst_mag_err_inner**2. + inst_mag_err_outer**2.)
544 # Set bad values to sentinel values for fgcm.
545 bad = ~np.isfinite(star_obs["delta_mag_aper"])
546 star_obs["delta_mag_aper"][bad] = 99.0
547 star_obs["delta_mag_err_aper"][bad] = 99.0
548
549 if self.config.doSubtractLocalBackground:
550 # At the moment we only adjust the flux and not the flux
551 # error by the background because the error on
552 # base_LocalBackground_instFlux is the rms error in the
553 # background annulus, not the error on the mean in the
554 # background estimate (which is much smaller, by sqrt(n)
555 # pixels used to estimate the background, which we do not
556 # have access to in this task). In the default settings,
557 # the annulus is sufficiently large such that these
558 # additional errors are negligibly small (much less
559 # than a mmag in quadrature).
560
561 # This is the difference between the mag with local background correction
562 # and the mag without local background correction.
563 local_background = local_background_area*sources[self.config.localBackgroundFluxField]
564 star_obs["delta_mag_bkg"] = (-2.5*np.log10(sources[self.config.instFluxField]
565 - local_background) -
566 -2.5*np.log10(sources[self.config.instFluxField]))
567
568 # Need to loop over detectors here.
569 for detector in camera:
570 detector_id = detector.getId()
571 # used index for all observations with a given detector
572 (use,) = (star_obs["detector"][obs_match] == detector_id).nonzero()
573 # Prior to running the calibration, we want to remove the effect
574 # of the jacobian of the WCS because that is a known quantity.
575 # Ideally, this would be done for each individual WCS, but this
576 # is extremely slow and makes small differences that are much
577 # smaller than the variation in the throughput due to other issues.
578 # Therefore, we use the approximate jacobian estimated from the
579 # camera model.
580 jac = approx_pixel_area_fields[detector_id].evaluate(
581 star_obs["x"][obs_match][use],
582 star_obs["y"][obs_match][use],
583 )
584 star_obs["jacobian"][obs_match[use]] = jac
585 scaled_inst_flux = (sources[self.config.instFluxField][obs_match[use]]
586 * visit_cat_table["scaling"][visit_match[use],
587 detector_mapping[detector_id]])
588 star_obs["inst_mag"][obs_match[use]] = (-2.5 * np.log10(scaled_inst_flux
589 / exp_time[use]))
590
591 # Compute instMagErr from inst_flux_err/inst_flux; scaling will cancel out.
592 star_obs["inst_mag_err"] = k*(sources[self.config.instFluxField + "Err"]
593 / sources[self.config.instFluxField])
594
595 # Apply the jacobian if configured to do so.
596 if self.config.doApplyWcsJacobian:
597 star_obs["inst_mag"] -= 2.5*np.log10(star_obs["jacobian"])
598
599 # We now reformat the stars and compute the ''objects'' that fgcm expects.
600 fgcm_obj = Table(data=np.zeros(len(stars), dtype=fgcm_obj_dtype))
601 fgcm_obj["isolated_star_id"] = stars["isolated_star_id"]
602 fgcm_obj["ra"] = stars["ra"]
603 fgcm_obj["dec"] = stars["dec"]
604 fgcm_obj["obs_arr_index"] = stars["source_cat_index"]
605 fgcm_obj["n_obs"] = stars["nsource"]
606
607 # Offset indexes to account for tract merging
608 fgcm_obj["obs_arr_index"] += merge_source_counter
609
610 fgcm_objs.append(fgcm_obj)
611 star_obs_cats.append(star_obs)
612
613 merge_source_counter += len(star_obs)
614
615 fgcm_obj = vstack(fgcm_objs)
616
617 # Set the fgcm_id to a unique 64-bit integer for easier sorting.
618 fgcm_obj["fgcm_id"][:] = np.arange(len(fgcm_obj)) + 1
619
620 return fgcm_obj, vstack(star_obs_cats)
621

◆ _mark_reserve_stars()

lsst.fgcmcal.fgcmBuildFromIsolatedStars.FgcmBuildFromIsolatedStarsTask._mark_reserve_stars ( self,
fgcm_obj )
protected
Run the star reservation task to flag reserved stars.

Parameters
----------
fgcm_obj : `astropy.table.Table`
    Catalog of per-star ids and positions.

Definition at line 674 of file fgcmBuildFromIsolatedStars.py.

674 def _mark_reserve_stars(self, fgcm_obj):
675 """Run the star reservation task to flag reserved stars.
676
677 Parameters
678 ----------
679 fgcm_obj : `astropy.table.Table`
680 Catalog of per-star ids and positions.
681 """
682 reserved = self.reserve_selection.run(
683 len(fgcm_obj),
684 extra=','.join(self.config.requiredBands),
685 )
686 fgcm_obj["obj_flag"][reserved] |= objFlagDict["RESERVED"]
687

◆ run()

lsst.fgcmcal.fgcmBuildFromIsolatedStars.FgcmBuildFromIsolatedStarsTask.run ( self,
* camera,
visit_summary_handle_dict,
isolated_star_cat_handle_dict,
isolated_star_source_handle_dict,
lookup_table_handle = None )
Run the fgcmBuildFromIsolatedStarsTask.

Parameters
----------
camera : `lsst.afw.cameraGeom.Camera`
    Camera object.
visit_summary_handle_dict : `dict` [`int`, [`lsst.daf.butler.DeferredDatasetHandle`]]
    Visit summary dataset handles, with the visit as key.
isolated_star_cat_handle_dict : `dict` [`int`, `lsst.daf.butler.DeferredDatasetHandle`]
    Isolated star catalog dataset handles, with the tract as key.
isolated_star_source_handle_dict : `dict` [`int`, `lsst.daf.butler.DeferredDatasetHandle`]
    Isolated star source dataset handles, with the tract as key.
lookup_table_handle : `lsst.daf.butler.DeferredDatasetHandle`, optional
    Data reference to fgcm look-up table (used if matching reference stars).

Returns
-------
struct : `lsst.pipe.base.struct`
    Catalogs for persistence, with attributes:

    ``fgcm_visit_catalog``
        Catalog of per-visit data (`lsst.afw.table.ExposureCatalog`).
    ``fgcm_star_observations``
        Catalog of individual star observations (`astropy.table.Table`).
    ``fgcm_star_ids``
        Catalog of per-star ids and positions (`astropy.table.Table`).
    ``fgcm_reference_stars``
        Catalog of reference stars matched to fgcm stars (`astropy.table.Table`).

Definition at line 258 of file fgcmBuildFromIsolatedStars.py.

259 isolated_star_source_handle_dict, lookup_table_handle=None):
260 """Run the fgcmBuildFromIsolatedStarsTask.
261
262 Parameters
263 ----------
264 camera : `lsst.afw.cameraGeom.Camera`
265 Camera object.
266 visit_summary_handle_dict : `dict` [`int`, [`lsst.daf.butler.DeferredDatasetHandle`]]
267 Visit summary dataset handles, with the visit as key.
268 isolated_star_cat_handle_dict : `dict` [`int`, `lsst.daf.butler.DeferredDatasetHandle`]
269 Isolated star catalog dataset handles, with the tract as key.
270 isolated_star_source_handle_dict : `dict` [`int`, `lsst.daf.butler.DeferredDatasetHandle`]
271 Isolated star source dataset handles, with the tract as key.
272 lookup_table_handle : `lsst.daf.butler.DeferredDatasetHandle`, optional
273 Data reference to fgcm look-up table (used if matching reference stars).
274
275 Returns
276 -------
277 struct : `lsst.pipe.base.struct`
278 Catalogs for persistence, with attributes:
279
280 ``fgcm_visit_catalog``
281 Catalog of per-visit data (`lsst.afw.table.ExposureCatalog`).
282 ``fgcm_star_observations``
283 Catalog of individual star observations (`astropy.table.Table`).
284 ``fgcm_star_ids``
285 Catalog of per-star ids and positions (`astropy.table.Table`).
286 ``fgcm_reference_stars``
287 Catalog of reference stars matched to fgcm stars (`astropy.table.Table`).
288 """
289 # Compute aperture radius if necessary. This is useful to do now before
290 # any heave lifting has happened (fail early).
291 calib_flux_aperture_radius = None
292 if self.config.doSubtractLocalBackground:
293 try:
294 calib_flux_aperture_radius = computeApertureRadiusFromName(self.config.instFluxField)
295 except RuntimeError as e:
296 raise RuntimeError("Could not determine aperture radius from %s. "
297 "Cannot use doSubtractLocalBackground." %
298 (self.config.instFluxField)) from e
299
300 # Check that we have the lookup_table_handle if we are doing reference matches.
301 if self.config.doReferenceMatches:
302 if lookup_table_handle is None:
303 raise RuntimeError("Must supply lookup_table_handle if config.doReferenceMatches is True.")
304
305 visit_cat = self.fgcmMakeVisitCatalog(camera, visit_summary_handle_dict)
306
307 # Select and concatenate the isolated stars and sources.
308 fgcm_obj, star_obs = self._make_all_star_obs_from_isolated_stars(
309 isolated_star_cat_handle_dict,
310 isolated_star_source_handle_dict,
311 visit_cat,
312 camera,
313 calib_flux_aperture_radius=calib_flux_aperture_radius,
314 )
315
316 self._compute_delta_aper_summary_statistics(
317 visit_cat,
318 star_obs,
319 )
320
321 # Do density down-sampling.
322 self._density_downsample(fgcm_obj, star_obs)
323
324 # Mark reserve stars
325 self._mark_reserve_stars(fgcm_obj)
326
327 # Do reference association.
328 if self.config.doReferenceMatches:
329 fgcm_ref = self._associate_reference_stars(lookup_table_handle, visit_cat, fgcm_obj)
330 else:
331 fgcm_ref = None
332
333 return pipeBase.Struct(
334 fgcm_visit_catalog=visit_cat,
335 fgcm_star_observations=star_obs,
336 fgcm_star_ids=fgcm_obj,
337 fgcm_reference_stars=fgcm_ref,
338 )
339

◆ runQuantum()

lsst.fgcmcal.fgcmBuildFromIsolatedStars.FgcmBuildFromIsolatedStarsTask.runQuantum ( self,
butlerQC,
inputRefs,
outputRefs )

Definition at line 197 of file fgcmBuildFromIsolatedStars.py.

197 def runQuantum(self, butlerQC, inputRefs, outputRefs):
198 input_ref_dict = butlerQC.get(inputRefs)
199
200 isolated_star_cat_handles = input_ref_dict["isolated_star_cats"]
201 isolated_star_source_handles = input_ref_dict["isolated_star_sources"]
202
203 isolated_star_cat_handle_dict = {
204 handle.dataId["tract"]: handle for handle in isolated_star_cat_handles
205 }
206 isolated_star_source_handle_dict = {
207 handle.dataId["tract"]: handle for handle in isolated_star_source_handles
208 }
209
210 if len(isolated_star_cat_handle_dict) != len(isolated_star_source_handle_dict):
211 raise RuntimeError("isolated_star_cats and isolate_star_sources must have same length.")
212
213 for tract in isolated_star_cat_handle_dict:
214 if tract not in isolated_star_source_handle_dict:
215 raise RuntimeError(f"tract {tract} in isolated_star_cats but not isolated_star_sources")
216
217 if self.config.doReferenceMatches:
218 lookup_table_handle = input_ref_dict["fgcm_lookup_table"]
219
220 # Prepare the reference catalog loader
221 ref_config = LoadReferenceObjectsConfig()
222 ref_config.filterMap = self.config.fgcmLoadReferenceCatalog.filterMap
223 ref_obj_loader = ReferenceObjectLoader(dataIds=[ref.datasetRef.dataId
224 for ref in inputRefs.ref_cat],
225 refCats=butlerQC.get(inputRefs.ref_cat),
226 name=self.config.connections.ref_cat,
227 log=self.log,
228 config=ref_config)
229 self.makeSubtask('fgcmLoadReferenceCatalog',
230 refObjLoader=ref_obj_loader,
231 refCatName=self.config.connections.ref_cat)
232 else:
233 lookup_table_handle = None
234
235 # The visit summary handles for use with fgcmMakeVisitCatalog must be keyed with
236 # visit, and values are a list with the first value being the visit_summary_handle,
237 # the second is the source catalog (which is not used, but a place-holder is
238 # left for compatibility reasons.)
239 visit_summary_handle_dict = {handle.dataId['visit']: [handle, None] for
240 handle in input_ref_dict['visit_summaries']}
241
242 camera = input_ref_dict["camera"]
243
244 struct = self.run(
245 camera=camera,
246 visit_summary_handle_dict=visit_summary_handle_dict,
247 isolated_star_cat_handle_dict=isolated_star_cat_handle_dict,
248 isolated_star_source_handle_dict=isolated_star_source_handle_dict,
249 lookup_table_handle=lookup_table_handle,
250 )
251
252 butlerQC.put(struct.fgcm_visit_catalog, outputRefs.fgcm_visit_catalog)
253 butlerQC.put(struct.fgcm_star_observations, outputRefs.fgcm_star_observations)
254 butlerQC.put(struct.fgcm_star_ids, outputRefs.fgcm_star_ids)
255 if self.config.doReferenceMatches:
256 butlerQC.put(struct.fgcm_reference_stars, outputRefs.fgcm_reference_stars)
257

Member Data Documentation

◆ _DefaultName

str lsst.fgcmcal.fgcmBuildFromIsolatedStars.FgcmBuildFromIsolatedStarsTask._DefaultName = "fgcmBuildFromIsolatedStars"
staticprotected

Definition at line 189 of file fgcmBuildFromIsolatedStars.py.

◆ canMultiprocess

bool lsst.fgcmcal.fgcmBuildFromIsolatedStars.FgcmBuildFromIsolatedStarsTask.canMultiprocess = False
static

Definition at line 191 of file fgcmBuildFromIsolatedStars.py.

◆ ConfigClass

lsst.fgcmcal.fgcmBuildFromIsolatedStars.FgcmBuildFromIsolatedStarsTask.ConfigClass = FgcmBuildFromIsolatedStarsConfig
static

Definition at line 188 of file fgcmBuildFromIsolatedStars.py.


The documentation for this class was generated from the following file: