22__all__ = [
"ColortermNotFoundError",
"Colorterm",
"ColortermDict",
"ColortermLibrary"]
28import astropy.units
as u
35 """Exception class indicating we couldn't find a colorterm
41 """Colorterm correction for one pair of filters
45 The transformed magnitude p' is given by:
46 p' = primary + c0 + c1*(primary - secondary) + c2*(primary - secondary)**2
48 To construct a Colorterm, use keyword arguments:
49 Colorterm(primary=primaryFilterName, secondary=secondaryFilterName, c0=c0value, c1=c1Coeff, c2=c2Coeff)
50 where c0-c2 are optional. For example (omitting c2):
51 Colorterm(primary="g", secondary=
"r", c0=-0.00816446, c1=-0.08366937)
53 This
is subclass of Config. That
is a bit of a hack to make it easy to store the data
54 in an appropriate obs_* package
as a config override file. In the long term some other
55 means of persistence will be used, at which point the constructor can be simplified
56 to
not require keyword arguments. (Fixing DM-2831 will also allow making a custom constructor).
58 primary = Field(dtype=str, doc="name of primary filter")
59 secondary = Field(dtype=str, doc=
"name of secondary filter")
60 c0 = Field(dtype=float, default=0.0, doc=
"Constant parameter")
61 c1 = Field(dtype=float, default=0.0, doc=
"First-order parameter")
62 c2 = Field(dtype=float, default=0.0, doc=
"Second-order parameter")
65 """Return the colorterm corrected magnitudes for a given filter.
70 The reference catalog to apply color corrections to.
71 filterName : `str`, deprecated
72 The camera filter to correct the reference catalog into.
73 The ``filterName`` argument is unused
and will be removed
in v23.
78 The corrected AB magnitudes.
79 refMagErr : `np.ndarray`
80 The corrected AB magnitude errors.
85 Raised
if the reference catalog does
not have a flux uncertainty
90 WARNING: I do
not know that we can trust the propagation of magnitude
91 errors returned by this method. They need more thorough tests.
93 if filterName !=
"deprecatedArgument":
94 msg =
"Colorterm.getCorrectedMagnitudes() `filterName` arg is unused and will be removed in v23."
95 warnings.warn(msg, category=FutureWarning, stacklevel=2)
97 def getFluxes(fluxField):
98 """Get the flux and fluxErr of this field from refCat.
103 Name of the source flux field to use.
108 refFluxErr : `Unknown`
113 Raised if reference catalog does
not have flux uncertainties
for the given flux field.
115 fluxKey = refCat.schema.find(fluxField).key
116 refFlux = refCat[fluxKey]
118 fluxErrKey = refCat.schema.find(fluxField +
"Err").key
119 refFluxErr = refCat[fluxErrKey]
120 except KeyError
as e:
121 raise KeyError(
"Reference catalog does not have flux uncertainties for %s" % fluxField)
from e
123 return refFlux, refFluxErr
125 primaryFlux, primaryErr = getFluxes(self.
primary +
"_flux")
126 secondaryFlux, secondaryErr = getFluxes(self.
secondary +
"_flux")
128 primaryMag = u.Quantity(primaryFlux, u.nJy).to_value(u.ABmag)
129 secondaryMag = u.Quantity(secondaryFlux, u.nJy).to_value(u.ABmag)
135 refMagErr = abMagErrFromFluxErr(refFluxErrArr*1e-9, primaryFlux*1e-9)
137 return refMag, refMagErr
140 """Transform the brightness of a source
145 Source whose brightness is to be converted; must support get(filterName)
146 (e.g. source.get(
"r")) method,
as do afw::table::Source
and dicts.
150 transformed : `float`
151 The transformed source magnitude.
156 """Transform brightness
161 Brightness in primary filter (magnitude).
163 Brightness
in secondary filter (magnitude).
167 transformed : `float`
168 The transformed brightness (
as a magnitude).
170 color = primary - secondary
171 return primary + self.
c0 + color*(self.
c1 + color*self.
c2)
174 return np.hypot((1 + self.
c1)*primaryFluxErr, self.
c1*secondaryFluxErr)
178 """A mapping of physical filter label to Colorterm
180 Different reference catalogs may need different ColortermDicts; see ColortermLibrary
182 To construct a ColortermDict use keyword arguments:
184 where dataDict is a Python dict of filterName: Colorterm
187 'g':
Colorterm(primary=
"g", secondary=
"r", c0=-0.00816446, c1=-0.08366937, c2=-0.00726883),
188 'r':
Colorterm(primary=
"r", secondary=
"i", c0= 0.00231810, c1= 0.01284177, c2=-0.03068248),
189 'i':
Colorterm(primary=
"i", secondary=
"z", c0= 0.00130204, c1=-0.16922042, c2=-0.01374245),
191 The constructor will likely be simplified at some point.
193 This
is subclass of Config. That
is a bit of a hack to make it easy to store the data
194 in an appropriate obs_* package
as a config override file. In the long term some other
195 means of persistence will be used, at which point the constructor can be made saner.
197 data = ConfigDictField(
198 doc="Mapping of filter name to Colorterm",
206 """A mapping of photometric reference catalog name or glob to ColortermDict
210 This allows photometric calibration using a variety of reference catalogs.
212 To construct a ColortermLibrary, use keyword arguments:
214 where dataDict is a Python dict of catalog_name_or_glob: ColortermDict
223 'g':
Colorterm(primary=
"g", secondary=
"g"),
224 'r':
Colorterm(primary=
"r", secondary=
"r"),
242 This
is subclass of Config. That
is a bit of a hack to make it easy to store the data
243 in an appropriate obs package
as a config override file. In the long term some other
244 means of persistence will be used, at which point the constructor can be made saner.
246 data = ConfigDictField(
247 doc="Mapping of reference catalog name (or glob) to ColortermDict",
249 itemtype=ColortermDict,
254 """Get the appropriate Colorterm from the library
256 Use dict of color terms in the library that matches the photoCatName.
257 If the photoCatName exactly matches an entry
in the library, that
258 dict
is used; otherwise
if the photoCatName matches a single glob (shell syntax,
259 e.g.,
"sdss-*" will match
"sdss-dr8"), then that
is used. If there
is no
260 exact match
and no unique match to the globs,
raise an exception.
264 physicalFilter : `str`
265 Label of physical filter to correct to.
267 Name of photometric reference catalog
from which to retrieve the data.
268 This argument
is not glob-expanded (but the catalog names
in the library are,
269 if no exact match
is found).
271 If
True then
raise ColortermNotFoundError
if no suitable Colorterm found;
272 If
False then
return a null Colorterm
with physicalFilter
as the primary
and secondary filter.
277 The appropriate Colorterm.
281 ColortermNotFoundError
282 If no suitable Colorterm found
and doRaise true;
283 other exceptions may be raised
for unexpected errors, regardless of the value of doRaise.
286 trueRefCatName =
None
287 ctDictConfig = self.
data.get(photoCatName)
288 if ctDictConfig
is None:
290 matchList = [libRefNameGlob
for libRefNameGlob
in self.
data
291 if fnmatch.fnmatch(photoCatName, libRefNameGlob)]
292 if len(matchList) == 1:
293 trueRefCatName = matchList[0]
294 ctDictConfig = self.
data[trueRefCatName]
295 elif len(matchList) > 1:
297 "Multiple library globs match photoCatName %r: %s" % (photoCatName, matchList))
300 "No colorterm dict found with photoCatName %r" % photoCatName)
301 ctDict = ctDictConfig.data
302 if physicalFilter
not in ctDict:
303 errMsg =
"No colorterm found for filter %r with photoCatName %r" % (
304 physicalFilter, photoCatName)
305 if trueRefCatName
is not None:
306 errMsg +=
" = catalog %r" % (trueRefCatName,)
308 return ctDict[physicalFilter]
309 except ColortermNotFoundError:
313 return Colorterm(physicalFilter, physicalFilter)
propagateFluxErrors(self, primaryFluxErr, secondaryFluxErr)
getCorrectedMagnitudes(self, refCat, filterName="deprecatedArgument")
transformSource(self, source)
transformMags(self, primary, secondary)
getColorterm(self, physicalFilter, photoCatName, doRaise=True)