lsst.pipe.tasks  13.0-66-gfbf2f2ce+5
colorterms.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008, 2009, 2010, 2011 LSST Corporation.
4 #
5 # This product includes software developed by the
6 # LSST Project (http://www.lsst.org/).
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the LSST License Statement and
19 # the GNU General Public License along with this program. If not,
20 # see <http://www.lsstcorp.org/LegalNotices/>.
21 #
22 
23 from __future__ import absolute_import, division, print_function
24 import fnmatch
25 
26 import numpy as np
27 
28 import lsst.pex.exceptions as pexExcept
29 from lsst.pex.config import Config, Field, ConfigDictField
30 from lsst.afw.image import Filter
31 
32 __all__ = ["ColortermNotFoundError", "Colorterm", "ColortermDict", "ColortermLibrary"]
33 
34 
35 class ColortermNotFoundError(LookupError):
36  """Exception class indicating we couldn't find a colorterm
37  """
38  pass
39 
40 
41 class Colorterm(Config):
42  """!Colorterm correction for one pair of filters
43 
44  The transformed magnitude p' is given by
45  p' = primary + c0 + c1*(primary - secondary) + c2*(primary - secondary)**2
46 
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)
51 
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).
56  """
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")
62 
63  def transformSource(self, source):
64  """!Transform the brightness of a source
65 
66  @param[in] source source whose brightness is to be converted; must support get(filterName)
67  (e.g. source.get("r")) method, as do afw::table::Source and dicts.
68  @return the transformed source magnitude
69  """
70  return self.transformMags(source.get(self.primary), source.get(self.secondary))
71 
72  def transformMags(self, primary, secondary):
73  """!Transform brightness
74 
75  @param[in] primary brightness in primary filter (magnitude)
76  @param[in] secondary brightness in secondary filter (magnitude)
77  @return the transformed brightness (as a magnitude)
78  """
79  color = primary - secondary
80  return primary + self.c0 + color*(self.c1 + color*self.c2)
81 
82  def propagateFluxErrors(self, primaryFluxErr, secondaryFluxErr):
83  return np.hypot((1 + self.c1)*primaryFluxErr, self.c1*secondaryFluxErr)
84 
85 
86 class ColortermDict(Config):
87  """!A mapping of filterName to Colorterm
88 
89  Different reference catalogs may need different ColortermDicts; see ColortermLibrary
90 
91  To construct a ColortermDict use keyword arguments:
92  ColortermDict(data=dataDict)
93  where dataDict is a Python dict of filterName: Colorterm
94  For example:
95  ColortermDict(data={
96  'g': Colorterm(primary="g", secondary="r", c0=-0.00816446, c1=-0.08366937, c2=-0.00726883),
97  'r': Colorterm(primary="r", secondary="i", c0= 0.00231810, c1= 0.01284177, c2=-0.03068248),
98  'i': Colorterm(primary="i", secondary="z", c0= 0.00130204, c1=-0.16922042, c2=-0.01374245),
99  })
100  The constructor will likely be simplified at some point.
101 
102  This is subclass of Config. That is a bit of a hack to make it easy to store the data
103  in an appropriate obs_* package as a config override file. In the long term some other
104  means of persistence will be used, at which point the constructor can be made saner.
105  """
106  data = ConfigDictField(
107  doc="Mapping of filter name to Colorterm",
108  keytype=str,
109  itemtype=Colorterm,
110  default={},
111  )
112 
113 
114 class ColortermLibrary(Config):
115  """!A mapping of photometric reference catalog name or glob to ColortermDict
116 
117  This allows photometric calibration using a variety of reference catalogs.
118 
119  To construct a ColortermLibrary, use keyword arguments:
120  ColortermLibrary(data=dataDict)
121  where dataDict is a Python dict of catalog_name_or_glob: ColortermDict
122 
123  For example:
124  ColortermLibrary(data = {
125  "hsc*": ColortermDict(data={
126  'g': Colorterm(primary="g", secondary="g"),
127  'r': Colorterm(primary="r", secondary="r"),
128  ...
129  }),
130  "sdss*": ColortermDict(data={
131  'g': Colorterm(primary="g", secondary="r", c0=-0.00816446, c1=-0.08366937, c2=-0.00726883),
132  'r': Colorterm(primary="r", secondary="i", c0= 0.00231810, c1= 0.01284177, c2=-0.03068248),
133  ...
134  }),
135  })
136 
137  This is subclass of Config. That is a bit of a hack to make it easy to store the data
138  in an appropriate obs_* package as a config override file. In the long term some other
139  means of persistence will be used, at which point the constructor can be made saner.
140  """
141  data = ConfigDictField(
142  doc="Mapping of reference catalog name (or glob) to ColortermDict",
143  keytype=str,
144  itemtype=ColortermDict,
145  default={},
146  )
147 
148  def getColorterm(self, filterName, photoCatName, doRaise=True):
149  """!Get the appropriate Colorterm from the library
150 
151  Use dict of color terms in the library that matches the photoCatName.
152  If the photoCatName exactly matches an entry in the library, that
153  dict is used; otherwise if the photoCatName matches a single glob (shell syntax,
154  e.g., "sdss-*" will match "sdss-dr8"), then that is used. If there is no
155  exact match and no unique match to the globs, raise an exception.
156 
157  @param filterName name of filter
158  @param photoCatName name of photometric reference catalog from which to retrieve the data.
159  This argument is not glob-expanded (but the catalog names in the library are,
160  if no exact match is found).
161  @param[in] doRaise if True then raise ColortermNotFoundError if no suitable Colorterm found;
162  if False then return a null Colorterm with filterName as the primary and secondary filter
163  @return the appropriate Colorterm
164 
165  @throw ColortermNotFoundError if no suitable Colorterm found and doRaise true;
166  other exceptions may be raised for unexpected errors, regardless of the value of doRaise
167  """
168  try:
169  trueRefCatName = None
170  ctDictConfig = self.data.get(photoCatName)
171  if ctDictConfig is None:
172  # try glob expression
173  matchList = [libRefNameGlob for libRefNameGlob in self.data
174  if fnmatch.fnmatch(photoCatName, libRefNameGlob)]
175  if len(matchList) == 1:
176  trueRefCatName = matchList[0]
177  ctDictConfig = self.data[trueRefCatName]
178  elif len(matchList) > 1:
179  raise ColortermNotFoundError(
180  "Multiple library globs match photoCatName %r: %s" % (photoCatName, matchList))
181  else:
182  raise ColortermNotFoundError(
183  "No colorterm dict found with photoCatName %r" % photoCatName)
184  ctDict = ctDictConfig.data
185  if filterName not in ctDict:
186  # Perhaps it's an alias
187  try:
188  filterName = Filter(Filter(filterName).getId()).getName()
189  except pexExcept.NotFoundError:
190  pass # this will be handled shortly
191  if filterName not in ctDict:
192  errMsg = "No colorterm found for filter %r with photoCatName %r" % (
193  filterName, photoCatName)
194  if trueRefCatName is not None:
195  errMsg += " = catalog %r" % (trueRefCatName,)
196  raise ColortermNotFoundError(errMsg)
197  return ctDict[filterName]
198  except ColortermNotFoundError:
199  if doRaise:
200  raise
201  else:
202  return Colorterm(filterName, filterName)
A mapping of filterName to Colorterm.
Definition: colorterms.py:86
A mapping of photometric reference catalog name or glob to ColortermDict.
Definition: colorterms.py:114
Colorterm correction for one pair of filters.
Definition: colorterms.py:41