@@ -151,6 +151,11 @@ def __init__(
151151 self .flags = []
152152 self .bkg_rms = []
153153 self .jacobs = []
154+ # Per-epoch full WCS and the object's sky position, used only by the
155+ # "wcs" centroid source (skipped for the default "hsm" path).
156+ self .wcs = []
157+ self .ra = []
158+ self .dec = []
154159 self .bkg_sub = bkg_sub
155160 self .megacam_flip = megacam_flip
156161
@@ -232,6 +237,12 @@ class Ngmix(object):
232237 id_obj_max : int, optional
233238 Last galaxy ID to process, not used if the value is set to ``-1``;
234239 the default is ``-1``
240+ centroid_source : {"hsm", "wcs"}, optional
241+ How to place the galaxy Jacobian origin for the centroid prior. The
242+ default ``"hsm"`` re-centers on the HSM adaptive-moment centroid
243+ (robust for galaxies); ``"wcs"`` uses the catalog sky position
244+ projected through the WCS (better for stars, whose HSM moments are
245+ noisy). See :func:`make_ngmix_observation`.
235246
236247 Raises
237248 ------
@@ -252,6 +263,7 @@ def __init__(
252263 save_batch = - 1 ,
253264 id_obj_min = - 1 ,
254265 id_obj_max = - 1 ,
266+ centroid_source = "hsm" ,
255267 ):
256268
257269 if len (input_file_list ) not in {6 , 7 }:
@@ -289,6 +301,7 @@ def __init__(
289301 self ._save_batch = save_batch
290302 self ._id_obj_min = id_obj_min
291303 self ._id_obj_max = id_obj_max
304+ self ._centroid_source = centroid_source
292305
293306 self ._w_log = w_log
294307
@@ -654,6 +667,7 @@ def process(self):
654667 prior ,
655668 flux_guess ,
656669 self ._rng ,
670+ centroid_source = self ._centroid_source ,
657671 )
658672 except Exception as ee :
659673 self ._w_log .info (
@@ -773,8 +787,9 @@ def prepare_postage_stamps(vignet, obj_id, i_tile, tile_cat):
773787 else None
774788 )
775789
790+ epoch_wcs = vignet .f_wcs_file [exp_name ][int (ccd_n )]['WCS' ]
776791 jacob = get_galsim_jacobian (
777- vignet . f_wcs_file [ exp_name ][ int ( ccd_n )][ 'WCS' ] ,
792+ epoch_wcs ,
778793 tile_cat .ra [i_tile ],
779794 tile_cat .dec [i_tile ]
780795 )
@@ -804,7 +819,11 @@ def prepare_postage_stamps(vignet, obj_id, i_tile, tile_cat):
804819 stamp .flags .append (flag_vign )
805820 stamp .bkg_rms .append (bkg_rms_vign_scaled )
806821 stamp .jacobs .append (jacob )
807-
822+ # For the "wcs" centroid source (see make_ngmix_observation).
823+ stamp .wcs .append (epoch_wcs )
824+ stamp .ra .append (tile_cat .ra [i_tile ])
825+ stamp .dec .append (tile_cat .dec [i_tile ])
826+
808827 return stamp
809828
810829def background_subtract (gal ,bkg ):
@@ -997,11 +1016,25 @@ def prepare_ngmix_weights(gal, weight, flag, rng, bkg_rms=None):
9971016
9981017 return gal_masked , weight_map , noise_img
9991018
1000- def make_ngmix_observation (gal , weight , flag , psf , wcs , rng , bkg_rms = None ):
1019+ def make_ngmix_observation (
1020+ gal , weight , flag , psf , wcs , rng ,
1021+ bkg_rms = None , centroid_source = "hsm" , wcs_full = None , ra = None , dec = None ,
1022+ ):
10011023 """Build an ngmix Observation for a single galaxy epoch.
10021024
1003- The galaxy Jacobian is re-centered on the HSM centroid so that the
1004- centroid prior (centered at the Jacobian origin) does not bias the fit.
1025+ The galaxy Jacobian origin sets where the centroid prior is centered, so
1026+ it must sit on the object. Two ways to place it, selected by
1027+ ``centroid_source``:
1028+
1029+ * ``"hsm"`` (default) — re-center on the HSM adaptive-moment centroid
1030+ measured from the stamp. Robust for **galaxies**: it follows the actual
1031+ light and so the centroid prior (centered at the Jacobian origin) does
1032+ not bias an object that is offset from the stamp center.
1033+ * ``"wcs"`` — place the origin at the object's catalog sky position,
1034+ projected through the WCS to a sub-pixel pixel offset from the stamp
1035+ center, with no shape measurement. Better for **stars**: their HSM
1036+ moments are noisy, so trusting the astrometry is more stable than
1037+ re-measuring the centroid.
10051038
10061039 Parameters
10071040 ----------
@@ -1016,6 +1049,14 @@ def make_ngmix_observation(gal, weight, flag, psf, wcs, rng, bkg_rms=None):
10161049 reproducibility).
10171050 bkg_rms : numpy.ndarray, optional
10181051 Per-pixel background RMS map.
1052+ centroid_source : {"hsm", "wcs"}, optional
1053+ How to place the galaxy Jacobian origin; the default is ``"hsm"``.
1054+ wcs_full : astropy.wcs.WCS, optional
1055+ Full exposure WCS for the object's CCD. Required for
1056+ ``centroid_source="wcs"`` (ignored for ``"hsm"``).
1057+ ra, dec : float, optional
1058+ Object sky position in degrees. Required for
1059+ ``centroid_source="wcs"`` (ignored for ``"hsm"``).
10191060
10201061 Returns
10211062 -------
@@ -1032,18 +1073,36 @@ def make_ngmix_observation(gal, weight, flag, psf, wcs, rng, bkg_rms=None):
10321073 gal , weight , flag , rng , bkg_rms = bkg_rms
10331074 )
10341075
1035- # Re-center Jacobian on HSM centroid (pixel offset from stamp center).
1036- # Fixes: centroid prior biases fit when galaxy is offset from stamp center.
1037- try :
1038- _hsm = galsim .hsm .FindAdaptiveMom (
1039- galsim .Image (gal , scale = 1.0 ), strict = False
1076+ if centroid_source == "hsm" :
1077+ # Re-center Jacobian on HSM centroid (pixel offset from stamp center).
1078+ # Fixes: centroid prior biases fit when galaxy is offset from stamp
1079+ # center. Robust for galaxies; noisy for stars (use "wcs" there).
1080+ try :
1081+ _hsm = galsim .hsm .FindAdaptiveMom (
1082+ galsim .Image (gal , scale = 1.0 ), strict = False
1083+ )
1084+ if _hsm .error_message != "" :
1085+ raise galsim .hsm .GalSimHSMError (_hsm .error_message )
1086+ _cen = _hsm .moments_centroid - galsim .Image (gal , scale = 1.0 ).center
1087+ cen_row , cen_col = _cen .y , _cen .x
1088+ except Exception :
1089+ cen_row , cen_col = 0.0 , 0.0
1090+ elif centroid_source == "wcs" :
1091+ # Place the origin at the catalog sky position projected through the
1092+ # WCS — no shape measurement. Stars have noisy HSM moments, so trust
1093+ # the astrometry instead of re-measuring the centroid.
1094+ g_wcs = galsim .fitswcs .AstropyWCS (wcs = wcs_full )
1095+ world_pos = galsim .CelestialCoord (
1096+ ra * galsim .degrees , dec * galsim .degrees
1097+ )
1098+ pos = g_wcs .toImage (world_pos )
1099+ cen_col = pos .x - np .round (pos .x ).astype (int )
1100+ cen_row = pos .y - np .round (pos .y ).astype (int )
1101+ else :
1102+ raise ValueError (
1103+ f"Unknown centroid_source '{ centroid_source } '; expected"
1104+ + " 'hsm' or 'wcs'"
10401105 )
1041- if _hsm .error_message != "" :
1042- raise galsim .hsm .GalSimHSMError (_hsm .error_message )
1043- _cen = _hsm .moments_centroid - galsim .Image (gal , scale = 1.0 ).center
1044- cen_row , cen_col = _cen .y , _cen .x
1045- except Exception :
1046- cen_row , cen_col = 0.0 , 0.0
10471106
10481107 gal_jacob = ngmix .Jacobian (
10491108 row = (gal .shape [0 ] - 1 ) / 2 + cen_row ,
@@ -1144,7 +1203,7 @@ def make_runners(prior, flux_guess, rng):
11441203 )
11451204
11461205
1147- def do_ngmix_metacal (stamp , prior , flux_guess , rng ):
1206+ def do_ngmix_metacal (stamp , prior , flux_guess , rng , centroid_source = "hsm" ):
11481207 """Do Ngmix Metacal.
11491208
11501209 Performs metacalibration on a single multi-epoch object and returns the
@@ -1160,6 +1219,12 @@ def do_ngmix_metacal(stamp, prior, flux_guess, rng):
11601219 Initial flux guess.
11611220 rng : numpy.random.RandomState
11621221 Random state for guesses and priors.
1222+ centroid_source : {"hsm", "wcs"}, optional
1223+ How to place the galaxy Jacobian origin; passed through to
1224+ :func:`make_ngmix_observation`. The default is ``"hsm"`` (HSM
1225+ adaptive-moment centroid); ``"wcs"`` uses the catalog sky position
1226+ projected through the WCS — see that function for the star-vs-galaxy
1227+ rationale.
11631228
11641229 Returns
11651230 -------
@@ -1182,6 +1247,10 @@ def do_ngmix_metacal(stamp, prior, flux_guess, rng):
11821247 stamp .jacobs [n_e ],
11831248 rng ,
11841249 bkg_rms = bkg_rms ,
1250+ centroid_source = centroid_source ,
1251+ wcs_full = stamp .wcs [n_e ] if n_e < len (stamp .wcs ) else None ,
1252+ ra = stamp .ra [n_e ] if n_e < len (stamp .ra ) else None ,
1253+ dec = stamp .dec [n_e ] if n_e < len (stamp .dec ) else None ,
11851254 )
11861255 gal_obs_list .append (gal_obs )
11871256
0 commit comments