25 import astropy.units
as u
32 __all__ = [
"ColortermNotFoundError",
"Colorterm",
"ColortermDict",
"ColortermLibrary"]
36 """Exception class indicating we couldn't find a colorterm 42 """!Colorterm correction for one pair of filters 44 The transformed magnitude p' is given by 45 p' = primary + c0 + c1*(primary - secondary) + c2*(primary - secondary)**2 47 To construct a Colorterm, use keyword arguments: 48 Colorterm(primary=primaryFilterName, secondary=secondaryFilterName, c0=c0value, c1=c1Coeff, c2=c2Coeff) 49 where c0-c2 are optional. For example (omitting c2): 50 Colorterm(primary="g", secondary="r", c0=-0.00816446, c1=-0.08366937) 52 This is subclass of Config. That
is a bit of a hack to make it easy to store the data
53 in an appropriate obs_* package
as a config override file. In the long term some other
54 means of persistence will be used, at which point the constructor can be simplified
55 to
not require keyword arguments. (Fixing DM-2831 will also allow making a custom constructor).
57 primary = Field(dtype=str, doc="name of primary filter") 58 secondary = Field(dtype=str, doc="name of secondary filter") 59 c0 = Field(dtype=float, default=0.0, doc="Constant parameter") 60 c1 = Field(dtype=float, default=0.0, doc="First-order parameter") 61 c2 = Field(dtype=float, default=0.0, doc="Second-order parameter") 63 def getCorrectedMagnitudes(self, refCat, filterName): 64 """Return the colorterm corrected magnitudes
for a given filter.
69 The reference catalog to apply color corrections to.
71 The camera filter to correct the reference catalog into.
76 The corrected AB magnitudes.
77 RefMagErr : `np.ndarray`
78 The corrected AB magnitude errors.
83 Raised
if the reference catalog does
not have a flux uncertainty
88 WARNING: I do
not know that we can trust the propagation of magnitude
89 errors returned by this method. They need more thorough tests.
92 def getFluxes(fluxField): 93 """Get the flux
and fluxErr of this field
from refCat.
""" 94 fluxKey = refCat.schema.find(fluxField).key 95 refFlux = refCat[fluxKey] 97 fluxErrKey = refCat.schema.find(fluxField + "Err").key 98 refFluxErr = refCat[fluxErrKey] 100 raise KeyError("Reference catalog does not have flux uncertainties for %s" % fluxField) from e 102 return refFlux, refFluxErr 104 primaryFlux, primaryErr = getFluxes(self.primary + "_flux") 105 secondaryFlux, secondaryErr = getFluxes(self.secondary + "_flux") 107 primaryMag = u.Quantity(primaryFlux, u.Jy).to_value(u.ABmag) 108 secondaryMag = u.Quantity(secondaryFlux, u.Jy).to_value(u.ABmag) 110 refMag = self.transformMags(primaryMag, secondaryMag) 111 refFluxErrArr = self.propagateFluxErrors(primaryErr, secondaryErr) 113 refMagErr = abMagErrFromFluxErr(refFluxErrArr, primaryFlux) 115 return refMag, refMagErr 117 def transformSource(self, source): 118 """!Transform the brightness of a source
120 @param[
in] source source whose brightness
is to be converted; must support get(filterName)
121 (e.g. source.get(
"r")) method, as do afw::table::Source and dicts. 122 @return the transformed source magnitude
124 return self.transformMags(source.get(self.primary), source.get(self.secondary)) 126 def transformMags(self, primary, secondary): 127 """!Transform brightness
129 @param[
in] primary brightness
in primary filter (magnitude)
130 @param[
in] secondary brightness
in secondary filter (magnitude)
131 @
return the transformed brightness (
as a magnitude)
133 color = primary - secondary 134 return primary + self.c0 + color*(self.c1 + color*self.c2) 136 def propagateFluxErrors(self, primaryFluxErr, secondaryFluxErr): 137 return np.hypot((1 + self.c1)*primaryFluxErr, self.c1*secondaryFluxErr) 140 class ColortermDict(Config): 141 """!A mapping of filterName to Colorterm
143 Different reference catalogs may need different ColortermDicts; see ColortermLibrary
145 To construct a ColortermDict use keyword arguments:
147 where dataDict
is a Python dict of filterName: Colorterm
150 'g':
Colorterm(primary=
"g", secondary=
"r", c0=-0.00816446, c1=-0.08366937, c2=-0.00726883), 151 'r': Colorterm(primary="r", secondary="i", c0= 0.00231810, c1= 0.01284177, c2=-0.03068248),
152 'i':
Colorterm(primary=
"i", secondary=
"z", c0= 0.00130204, c1=-0.16922042, c2=-0.01374245),
154 The constructor will likely be simplified at some point.
156 This
is subclass of Config. That
is a bit of a hack to make it easy to store the data
157 in an appropriate obs_* package
as a config override file. In the long term some other
158 means of persistence will be used, at which point the constructor can be made saner.
160 data = ConfigDictField( 161 doc="Mapping of filter name to Colorterm", 168 class ColortermLibrary(Config): 169 """!A mapping of photometric reference catalog name
or glob to ColortermDict
171 This allows photometric calibration using a variety of reference catalogs.
173 To construct a ColortermLibrary, use keyword arguments:
175 where dataDict
is a Python dict of catalog_name_or_glob: ColortermDict
180 'g':
Colorterm(primary=
"g", secondary=
"g"),
181 'r': Colorterm(primary="r", secondary="r"), 185 'g':
Colorterm(primary=
"g", secondary=
"r", c0=-0.00816446, c1=-0.08366937, c2=-0.00726883), 186 'r': Colorterm(primary="r", secondary="i", c0= 0.00231810, c1= 0.01284177, c2=-0.03068248),
191 This
is subclass of Config. That
is a bit of a hack to make it easy to store the data
192 in an appropriate obs_* package
as a config override file. In the long term some other
193 means of persistence will be used, at which point the constructor can be made saner.
195 data = ConfigDictField( 196 doc="Mapping of reference catalog name (or glob) to ColortermDict", 198 itemtype=ColortermDict, 202 def getColorterm(self, filterName, photoCatName, doRaise=True): 203 """!Get the appropriate Colorterm
from the library
205 Use dict of color terms
in the library that matches the photoCatName.
206 If the photoCatName exactly matches an entry
in the library, that
207 dict
is used; otherwise
if the photoCatName matches a single glob (shell syntax,
208 e.g.,
"sdss-*" will match
"sdss-dr8"), then that
is used. If there
is no
209 exact match
and no unique match to the globs,
raise an exception.
211 @param filterName name of filter
212 @param photoCatName name of photometric reference catalog
from which to retrieve the data.
213 This argument
is not glob-expanded (but the catalog names
in the library are,
214 if no exact match
is found).
215 @param[
in] doRaise
if True then
raise ColortermNotFoundError
if no suitable Colorterm found;
216 if False then
return a null Colorterm with filterName
as the primary
and secondary filter
217 @
return the appropriate Colorterm
219 @throw ColortermNotFoundError
if no suitable Colorterm found
and doRaise true;
220 other exceptions may be raised
for unexpected errors, regardless of the value of doRaise
223 trueRefCatName = None 224 ctDictConfig = self.data.get(photoCatName) 225 if ctDictConfig is None: 226 # try glob expression 227 matchList = [libRefNameGlob for libRefNameGlob in self.data 228 if fnmatch.fnmatch(photoCatName, libRefNameGlob)] 229 if len(matchList) == 1: 230 trueRefCatName = matchList[0] 231 ctDictConfig = self.data[trueRefCatName] 232 elif len(matchList) > 1: 233 raise ColortermNotFoundError( 234 "Multiple library globs match photoCatName %r: %s" % (photoCatName, matchList)) 236 raise ColortermNotFoundError( 237 "No colorterm dict found with photoCatName %r" % photoCatName) 238 ctDict = ctDictConfig.data 239 if filterName not in ctDict: 240 # Perhaps it's an alias 242 filterName = Filter(Filter(filterName).getId()).getName() 243 except pexExcept.NotFoundError: 244 pass # this will be handled shortly 245 if filterName not in ctDict: 246 errMsg = "No colorterm found for filter %r with photoCatName %r" % ( 247 filterName, photoCatName) 248 if trueRefCatName is not None: 249 errMsg += " = catalog %r" % (trueRefCatName,) 250 raise ColortermNotFoundError(errMsg) 251 return ctDict[filterName] 252 except ColortermNotFoundError: 256 return Colorterm(filterName, filterName)
A mapping of filterName to Colorterm.
A mapping of photometric reference catalog name or glob to ColortermDict.
Colorterm correction for one pair of filters.