LSSTApplications  10.0-2-g4f67435,11.0.rc2+1,11.0.rc2+12,11.0.rc2+3,11.0.rc2+4,11.0.rc2+5,11.0.rc2+6,11.0.rc2+7,11.0.rc2+8
LSSTDataManagementBasePackage
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 import fnmatch
24 
25 import numpy as np
26 
27 import lsst.pex.exceptions as pexExcept
28 from lsst.pex.config import Config, Field, ConfigDictField
29 from lsst.afw.image import Filter
30 
31 __all__ = ["ColortermNotFoundError", "Colorterm", "ColortermDict", "ColortermLibrary"]
32 
33 class ColortermNotFoundError(LookupError):
34  """Exception class indicating we couldn't find a colorterm
35  """
36  pass
37 
38 
39 class Colorterm(Config):
40  """!Colorterm correction for one pair of filters
41 
42  The transformed magnitude p' is given by
43  p' = primary + c0 + c1*(primary - secondary) + c2*(primary - secondary)**2
44 
45  To construct a Colorterm, use keyword arguments:
46  Colorterm(primary=primaryFilterName, secondary=secondaryFilterName, c0=c0value, c1=c1Coeff, c2=c2Coeff)
47  where c0-c2 are optional. For example (omitting c2):
48  Colorterm(primary="g", secondary="r", c0=-0.00816446, c1=-0.08366937)
49 
50  This is subclass of Config. That is a bit of a hack to make it easy to store the data
51  in an appropriate obs_* package as a config override file. In the long term some other
52  means of persistence will be used, at which point the constructor can be simplified
53  to not require keyword arguments. (Fixing DM-2831 will also allow making a custom constructor).
54  """
55  primary = Field(dtype=str, doc="name of primary filter")
56  secondary = Field(dtype=str, doc="name of secondary filter")
57  c0 = Field(dtype=float, default=0.0, doc="Constant parameter")
58  c1 = Field(dtype=float, default=0.0, doc="First-order parameter")
59  c2 = Field(dtype=float, default=0.0, doc="Second-order parameter")
60 
61  def transformSource(self, source):
62  """!Transform the brightness of a source
63 
64  @param[in] source source whose brightness is to be converted; must support get(filterName)
65  (e.g. source.get("r")) method, as do afw::table::Source and dicts.
66  @return the transformed source magnitude
67  """
68  return self.transformMags(source.get(self.primary), source.get(self.secondary))
69 
70  def transformMags(self, primary, secondary):
71  """!Transform brightness
72 
73  @param[in] primary brightness in primary filter (magnitude)
74  @param[in] secondary brightness in secondary filter (magnitude)
75  @return the transformed brightness (as a magnitude)
76  """
77  color = primary - secondary
78  return primary + self.c0 + color*(self.c1 + color*self.c2)
79 
80  def propagateFluxErrors(self, primaryFluxErr, secondaryFluxErr):
81  return np.hypot((1 + self.c1)*primaryFluxErr, self.c1*secondaryFluxErr)
82 
83 
84 class ColortermDict(Config):
85  """!A mapping of filterName to Colorterm
86 
87  Different reference catalogs may need different ColortermDicts; see ColortermLibrary
88 
89  To construct a ColortermDict use keyword arguments:
90  ColortermDict(data=dataDict)
91  where dataDict is a Python dict of filterName: Colorterm
92  For example:
93  ColortermDict(data={
94  'g': Colorterm(primary="g", secondary="r", c0=-0.00816446, c1=-0.08366937, c2=-0.00726883),
95  'r': Colorterm(primary="r", secondary="i", c0= 0.00231810, c1= 0.01284177, c2=-0.03068248),
96  'i': Colorterm(primary="i", secondary="z", c0= 0.00130204, c1=-0.16922042, c2=-0.01374245),
97  })
98  The constructor will likely be simplified at some point.
99 
100  This is subclass of Config. That is a bit of a hack to make it easy to store the data
101  in an appropriate obs_* package as a config override file. In the long term some other
102  means of persistence will be used, at which point the constructor can be made saner.
103  """
104  data = ConfigDictField(
105  doc="Mapping of filter name to Colorterm",
106  keytype=str,
107  itemtype=Colorterm,
108  default={},
109  )
110 
111 
112 class ColortermLibrary(Config):
113  """!A mapping of photometric reference catalog name or glob to ColortermDict
114 
115  This allows photometric calibration using a variety of reference catalogs.
116 
117  To construct a ColortermLibrary, use keyword arguments:
118  ColortermLibrary(data=dataDict)
119  where dataDict is a Python dict of catalog_name_or_glob: ColortermDict
120 
121  For example:
122  ColortermLibrary(data = {
123  "hsc*": ColortermDict(data={
124  'g': Colorterm(primary="g", secondary="g"),
125  'r': Colorterm(primary="r", secondary="r"),
126  ...
127  }),
128  "sdss*": ColortermDict(data={
129  'g': Colorterm(primary="g", secondary="r", c0=-0.00816446, c1=-0.08366937, c2=-0.00726883),
130  'r': Colorterm(primary="r", secondary="i", c0= 0.00231810, c1= 0.01284177, c2=-0.03068248),
131  ...
132  }),
133  })
134 
135  This is subclass of Config. That is a bit of a hack to make it easy to store the data
136  in an appropriate obs_* package as a config override file. In the long term some other
137  means of persistence will be used, at which point the constructor can be made saner.
138  """
139  data = ConfigDictField(
140  doc="Mapping of reference catalog name (or glob) to ColortermDict",
141  keytype=str,
142  itemtype=ColortermDict,
143  default={},
144  )
145 
146  def getColorterm(self, filterName, photoCatName, doRaise=True):
147  """!Get the appropriate Colorterm from the library
148 
149  Use dict of color terms in the library that matches the photoCatName.
150  If the photoCatName exactly matches an entry in the library, that
151  dict is used; otherwise if the photoCatName matches a single glob (shell syntax,
152  e.g., "sdss-*" will match "sdss-dr8"), then that is used. If there is no
153  exact match and no unique match to the globs, raise an exception.
154 
155  @param filterName name of filter
156  @param photoCatName name of photometric reference catalog from which to retrieve the data.
157  This argument is not glob-expanded (but the catalog names in the library are,
158  if no exact match is found).
159  @param[in] doRaise if True then raise ColortermNotFoundError if no suitable Colorterm found;
160  if False then return a null Colorterm with filterName as the primary and secondary filter
161  @return the appropriate Colorterm
162 
163  @throw ColortermNotFoundError if no suitable Colorterm found and doRaise true;
164  other exceptions may be raised for unexpected errors, regardless of the value of doRaise
165  """
166  try:
167  trueRefCatName = None
168  ctDictConfig = self.data.get(photoCatName)
169  if ctDictConfig is None:
170  # try glob expression
171  matchList = [libRefNameGlob for libRefNameGlob in self.data
172  if fnmatch.fnmatch(photoCatName, libRefNameGlob)]
173  if len(matchList) == 1:
174  trueRefCatName = matchList[0]
175  ctDictConfig = self.data[trueRefCatName]
176  elif len(matchList) > 1:
177  raise ColortermNotFoundError(
178  "Multiple library globs match photoCatName %r: %s" % (photoCatName, matchList))
179  else:
180  raise ColortermNotFoundError("No colorterm dict found with photoCatName %r" % photoCatName)
181  ctDict = ctDictConfig.data
182  if filterName not in ctDict:
183  # Perhaps it's an alias
184  try:
185  filterName = Filter(Filter(filterName).getId()).getName()
186  except pexExcept.NotFoundError:
187  pass # this will be handled shortly
188  if filterName not in ctDict:
189  errMsg = "No colorterm found for filter %r with photoCatName %r" % (filterName, photoCatName)
190  if trueRefCatName is not None:
191  errMsg += " = catalog %r" % (trueRefCatName,)
192  raise ColortermNotFoundError(errMsg)
193  return ctDict[filterName]
194  except ColortermNotFoundError:
195  if doRaise:
196  raise
197  else:
198  return Colorterm(filterName, filterName)
A mapping of filterName to Colorterm.
Definition: colorterms.py:84
A mapping of photometric reference catalog name or glob to ColortermDict.
Definition: colorterms.py:112
Colorterm correction for one pair of filters.
Definition: colorterms.py:39