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")
64 """Return the colorterm corrected magnitudes for a given filter.
68 refCat : `lsst.afw.table.SimpleCatalog`
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.
primaryprimary +
"_flux")
105 secondaryFlux, secondaryErr = getFluxes(self.
secondarysecondary +
"_flux")
107 primaryMag = u.Quantity(primaryFlux, u.nJy).to_value(u.ABmag)
108 secondaryMag = u.Quantity(secondaryFlux, u.nJy).to_value(u.ABmag)
110 refMag = self.
transformMagstransformMags(primaryMag, secondaryMag)
114 refMagErr = abMagErrFromFluxErr(refFluxErrArr*1e-9, primaryFlux*1e-9)
116 return refMag, refMagErr
119 """!Transform the brightness of a source
121 @param[in] source source whose brightness is to be converted; must support get(filterName)
122 (e.g. source.get("r")) method, as do afw::table::Source and dicts.
123 @return the transformed source magnitude
128 """!Transform brightness
130 @param[in] primary brightness in primary filter (magnitude)
131 @param[in] secondary brightness in secondary filter (magnitude)
132 @return the transformed brightness (as a magnitude)
134 color = primary - secondary
135 return primary + self.
c0c0 + color*(self.
c1c1 + color*self.
c2c2)
138 return np.hypot((1 + self.
c1c1)*primaryFluxErr, self.
c1c1*secondaryFluxErr)
142 """!A mapping of filterName to Colorterm
144 Different reference catalogs may need different ColortermDicts; see ColortermLibrary
146 To construct a ColortermDict use keyword arguments:
147 ColortermDict(data=dataDict)
148 where dataDict is a Python dict of filterName: Colorterm
151 'g': Colorterm(primary="g", secondary="r", c0=-0.00816446, c1=-0.08366937, c2=-0.00726883),
152 'r': Colorterm(primary="r", secondary="i", c0= 0.00231810, c1= 0.01284177, c2=-0.03068248),
153 'i': Colorterm(primary="i", secondary="z", c0= 0.00130204, c1=-0.16922042, c2=-0.01374245),
155 The constructor will likely be simplified at some point.
157 This is subclass of Config. That is a bit of a hack to make it easy to store the data
158 in an appropriate obs_* package as a config override file. In the long term some other
159 means of persistence will be used, at which point the constructor can be made saner.
161 data = ConfigDictField(
162 doc=
"Mapping of filter name to Colorterm",
170 """!A mapping of photometric reference catalog name or glob to ColortermDict
172 This allows photometric calibration using a variety of reference catalogs.
174 To construct a ColortermLibrary, use keyword arguments:
175 ColortermLibrary(data=dataDict)
176 where dataDict is a Python dict of catalog_name_or_glob: ColortermDict
179 ColortermLibrary(data = {
180 "hsc*": ColortermDict(data={
181 'g': Colorterm(primary="g", secondary="g"),
182 'r': Colorterm(primary="r", secondary="r"),
185 "sdss*": ColortermDict(data={
186 'g': Colorterm(primary="g", secondary="r", c0=-0.00816446, c1=-0.08366937, c2=-0.00726883),
187 'r': Colorterm(primary="r", secondary="i", c0= 0.00231810, c1= 0.01284177, c2=-0.03068248),
192 This is subclass of Config. That is a bit of a hack to make it easy to store the data
193 in an appropriate obs_* package as a config override file. In the long term some other
194 means of persistence will be used, at which point the constructor can be made saner.
196 data = ConfigDictField(
197 doc=
"Mapping of reference catalog name (or glob) to ColortermDict",
199 itemtype=ColortermDict,
204 """!Get the appropriate Colorterm from the library
206 Use dict of color terms in the library that matches the photoCatName.
207 If the photoCatName exactly matches an entry in the library, that
208 dict is used; otherwise if the photoCatName matches a single glob (shell syntax,
209 e.g., "sdss-*" will match "sdss-dr8"), then that is used. If there is no
210 exact match and no unique match to the globs, raise an exception.
212 @param filterName name of filter
213 @param photoCatName name of photometric reference catalog from which to retrieve the data.
214 This argument is not glob-expanded (but the catalog names in the library are,
215 if no exact match is found).
216 @param[in] doRaise if True then raise ColortermNotFoundError if no suitable Colorterm found;
217 if False then return a null Colorterm with filterName as the primary and secondary filter
218 @return the appropriate Colorterm
220 @throw ColortermNotFoundError if no suitable Colorterm found and doRaise true;
221 other exceptions may be raised for unexpected errors, regardless of the value of doRaise
224 trueRefCatName =
None
225 ctDictConfig = self.
datadata.get(photoCatName)
226 if ctDictConfig
is None:
228 matchList = [libRefNameGlob
for libRefNameGlob
in self.
datadata
229 if fnmatch.fnmatch(photoCatName, libRefNameGlob)]
230 if len(matchList) == 1:
231 trueRefCatName = matchList[0]
232 ctDictConfig = self.
datadata[trueRefCatName]
233 elif len(matchList) > 1:
235 "Multiple library globs match photoCatName %r: %s" % (photoCatName, matchList))
238 "No colorterm dict found with photoCatName %r" % photoCatName)
239 ctDict = ctDictConfig.data
240 if filterName
not in ctDict:
243 filterName =
Filter(
Filter(filterName).getId()).getName()
244 except pexExcept.NotFoundError:
246 if filterName
not in ctDict:
247 errMsg =
"No colorterm found for filter %r with photoCatName %r" % (
248 filterName, photoCatName)
249 if trueRefCatName
is not None:
250 errMsg +=
" = catalog %r" % (trueRefCatName,)
252 return ctDict[filterName]
253 except ColortermNotFoundError:
A mapping of filterName to Colorterm.
Colorterm correction for one pair of filters.
def propagateFluxErrors(self, primaryFluxErr, secondaryFluxErr)
def getCorrectedMagnitudes(self, refCat, filterName)
def transformMags(self, primary, secondary)
Transform brightness.
def transformSource(self, source)
Transform the brightness of a source.
A mapping of photometric reference catalog name or glob to ColortermDict.
def getColorterm(self, filterName, photoCatName, doRaise=True)
Get the appropriate Colorterm from the library.