LSSTApplications  16.0-10-g0ee56ad+5,16.0-11-ga33d1f2+5,16.0-12-g3ef5c14+3,16.0-12-g71e5ef5+18,16.0-12-gbdf3636+3,16.0-13-g118c103+3,16.0-13-g8f68b0a+3,16.0-15-gbf5c1cb+4,16.0-16-gfd17674+3,16.0-17-g7c01f5c+3,16.0-18-g0a50484+1,16.0-20-ga20f992+8,16.0-21-g0e05fd4+6,16.0-21-g15e2d33+4,16.0-22-g62d8060+4,16.0-22-g847a80f+4,16.0-25-gf00d9b8+1,16.0-28-g3990c221+4,16.0-3-gf928089+3,16.0-32-g88a4f23+5,16.0-34-gd7987ad+3,16.0-37-gc7333cb+2,16.0-4-g10fc685+2,16.0-4-g18f3627+26,16.0-4-g5f3a788+26,16.0-5-gaf5c3d7+4,16.0-5-gcc1f4bb+1,16.0-6-g3b92700+4,16.0-6-g4412fcd+3,16.0-6-g7235603+4,16.0-69-g2562ce1b+2,16.0-8-g14ebd58+4,16.0-8-g2df868b+1,16.0-8-g4cec79c+6,16.0-8-gadf6c7a+1,16.0-8-gfc7ad86,16.0-82-g59ec2a54a+1,16.0-9-g5400cdc+2,16.0-9-ge6233d7+5,master-g2880f2d8cf+3,v17.0.rc1
LSSTDataManagementBasePackage
tractor.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008-2013 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 """Code to load multi-Gaussian approximations to profiles from "The Tractor"
23 into a lsst.shapelet.MultiShapeletBasis.
24 
25 Please see the README file in the data directory of the lsst.shapelet
26 package for more information.
27 """
28 from future import standard_library
29 standard_library.install_aliases() # noqa
30 from builtins import zip
31 from builtins import str
32 from builtins import range
33 
34 import numpy
35 import os
36 import re
37 import sys
38 import warnings
39 import pickle
40 
42 from .radialProfile import RadialProfile
43 from .multiShapeletBasis import MultiShapeletBasis
44 from .shapeletFunction import ShapeletFunction
45 
46 
48  """Register the pickled profiles in the data directory with the RadialProfile
49  singleton registry.
50 
51  This should only be called at import time by this module; it's only a function to
52  avoid polluting the module namespace with all the local variables used here.
53  """
54  dataDir = os.path.join(os.environ["SHAPELET_DIR"], "data")
55  regex = re.compile(r"([a-z]+\d?)_K(\d+)_MR(\d+)\.pickle")
56  for filename in os.listdir(dataDir):
57  match = regex.match(filename)
58  if not match:
59  continue
60  name = match.group(1)
61  nComponents = int(match.group(2))
62  maxRadius = int(match.group(3))
63  try:
64  profile = RadialProfile.get(name)
66  warnings.warn("No C++ profile for multi-Gaussian pickle file '%s'" % filename)
67  continue
68  with open(os.path.join(dataDir, filename), 'rb') as stream:
69  if sys.version_info[0] >= 3:
70  array = pickle.load(stream, encoding='latin1')
71  else:
72  array = pickle.load(stream)
73  amplitudes = array[:nComponents]
74  amplitudes /= amplitudes.sum()
75  variances = array[nComponents:]
76  if amplitudes.shape != (nComponents,) or variances.shape != (nComponents,):
77  warnings.warn("Unknown format for multi-Gaussian pickle file '%s'" % filename)
78  continue
79  basis = MultiShapeletBasis(1)
80  for amplitude, variance in zip(amplitudes, variances):
81  radius = variance**0.5
82  matrix = numpy.array([[amplitude / ShapeletFunction.FLUX_FACTOR]], dtype=float)
83  basis.addComponent(radius, 0, matrix)
84  profile.registerBasis(basis, nComponents, maxRadius)
85 
86 
87 # We register all the profiles at module import time, to allow C++ code to access all available profiles
88 # without having to later call Python code to unpickle them.
90 
91 
92 def evaluateRadial(basis, r, sbNormalize=False, doComponents=False):
93  """Plot a single-element MultiShapeletBasis as a radial profile.
94 
95  Parameters
96  ----------
97  sbNormalize : `bool`
98  `True` to normalize.
99  doComponents : `bool`
100  `True` to evaluate components.
101  """
103  coefficients = numpy.ones(1, dtype=float)
104  msf = basis.makeFunction(ellipse, coefficients)
105  ev = msf.evaluate()
106  n = 1
107  if doComponents:
108  n += len(msf.getComponents())
109  z = numpy.zeros((n,) + r.shape, dtype=float)
110  for j, x in enumerate(r):
111  z[0, j] = ev(x, 0.0)
112  if doComponents:
113  for i, sf in enumerate(msf.getComponents()):
114  evc = sf.evaluate()
115  for j, x in enumerate(r):
116  z[i+1, j] = evc(x, 0.0)
117  if sbNormalize:
118  z /= ev(1.0, 0.0)
119  return z
120 
121 
122 def integrateNormalizedFluxes(maxRadius=20.0, nSteps=5000):
123  """Integrate the profiles to compare relative fluxes between the true profiles
124  and their approximations.
125 
126  After normalizing by surface brightness at r=1 r_e, integrate the profiles to compare
127  relative fluxes between the true profiles and their approximations.
128 
129  Parameters
130  ----------
131  maxRadius : `float`, optional
132  Maximum radius to integrate the profile, in units of r_e.
133  nSteps : `int`, optional
134  Number of concrete points at which to evaluate the profile to
135  do the integration (we just use the trapezoidal rule).
136 
137  Returns
138  -------
139  fluxes : `dict` of `float` values
140  Dictionary of fluxes (``exp``, ``lux``, ``dev``, ``luv``, ``ser2``, ``ser3``,
141  ``ser5``, ``gexp``, ``glux``, ``gdev``, ``gluv``, ``gser2``, ``gser3``, ``gser5``)
142  """
143  radii = numpy.linspace(0.0, maxRadius, nSteps)
144  profiles = {name: RadialProfile.get(name) for name in ("exp", "lux", "dev", "luv",
145  "ser2", "ser3", "ser5")}
146  evaluated = {}
147  for name, profile in profiles.items():
148  evaluated[name] = profile.evaluate(radii)
149  basis = profile.getBasis(8)
150  evaluated["g" + name] = evaluateRadial(basis, radii, sbNormalize=True, doComponents=False)[0, :]
151  fluxes = {name: numpy.trapz(z*radii, radii) for name, z in evaluated.items()}
152  return fluxes
153 
154 
155 def plotSuite(doComponents=False):
156  """Plot all the profiles defined in this module together.
157 
158  Plot all the profiles defined in this module together: true exp and dev,
159  the SDSS softened/truncated lux and luv, and the multi-Gaussian approximations
160  to all of these.
161 
162  Parameters
163  ----------
164  doComponents : `bool`, optional
165  True, to plot the individual Gaussians that form the multi-Gaussian approximations.
166 
167  Returns
168  -------
169  figure : `matplotlib.figure.Figure`
170  Figure that contains the plot.
171  axes : `numpy.ndarray` of `matplotlib.axes.Axes`
172  A 2x4 NumPy array of matplotlib axes objects.
173  """
174  from matplotlib import pyplot
175  fig = pyplot.figure(figsize=(9, 4.7))
176  axes = numpy.zeros((2, 4), dtype=object)
177  r1 = numpy.logspace(-3, 0, 1000, base=10)
178  r2 = numpy.linspace(1, 10, 1000)
179  r = [r1, r2]
180  for i in range(2):
181  for j in range(4):
182  axes[i, j] = fig.add_subplot(2, 4, i*4+j+1)
183  profiles = {name: RadialProfile.get(name) for name in ("exp", "lux", "dev", "luv")}
184  basis = {name: profiles[name].getBasis(8) for name in profiles}
185  z = numpy.zeros((2, 4), dtype=object)
186  colors = ("k", "g", "b", "r")
187  fig.subplots_adjust(wspace=0.025, hspace=0.025, bottom=0.15, left=0.1, right=0.98, top=0.92)
188  centers = [None, None]
189  for i in range(2): # 0=profile, 1=relative error
190  for j in range(0, 4, 2): # grid columns: 0=exp-like, 2=dev-like
191  bbox0 = axes[i, j].get_position()
192  bbox1 = axes[i, j+1].get_position()
193  bbox1.x0 = bbox0.x1 - 0.06
194  bbox0.x1 = bbox1.x0
195  centers[j/2] = 0.5*(bbox0.x0 + bbox1.x1)
196  axes[i, j].set_position(bbox0)
197  axes[i, j+1].set_position(bbox1)
198  for j in range(0, 2):
199  z[0, j] = [evaluateRadial(basis[k], r[j], sbNormalize=True, doComponents=doComponents)
200  for k in ("exp", "lux")]
201  z[0, j][0:0] = [profiles[k].evaluate(r[j])[numpy.newaxis, :] for k in ("exp", "lux")]
202  z[0, j+2] = [evaluateRadial(basis[k], r[j], sbNormalize=True, doComponents=doComponents)
203  for k in ("dev", "luv")]
204  z[0, j+2][0:0] = [profiles[k].evaluate(r[j])[numpy.newaxis, :] for k in ("dev", "luv")]
205  methodNames = [["loglog", "semilogy"], ["semilogx", "plot"]]
206  for j in range(0, 4): # grid columns
207  z[1, j] = [(z[0, j][0][0, :] - z[0, j][i][0, :])/z[0, j][0][0, :] for i in range(0, 4)]
208  handles = []
209  method0 = getattr(axes[0, j], methodNames[0][j%2])
210  method1 = getattr(axes[1, j], methodNames[1][j%2])
211  for k in range(4):
212  y0 = z[0, j][k]
213  handles.append(method0(r[j%2], y0[0, :], color=colors[k])[0])
214  if doComponents:
215  for l in range(1, y0.shape[0]):
216  method0(r[j%2], y0[l, :], color=colors[k], alpha=0.25)
217  method1(r[j%2], z[1, j][k], color=colors[k])
218  axes[0, j].set_xticklabels([])
219  axes[0, j].set_ylim(1E-6, 1E3)
220  axes[1, j].set_ylim(-0.2, 1.0)
221  for i, label in enumerate(("profile", "relative error")):
222  axes[i, 0].set_ylabel(label)
223  for t in axes[i, 0].get_yticklabels():
224  t.set_fontsize(11)
225  for j in range(1, 4):
226  axes[0, j].set_yticklabels([])
227  axes[1, j].set_yticklabels([])
228  xticks = [['$\\mathdefault{10^{%d}}$' % i for i in range(-3, 1)],
229  [str(i) for i in range(1, 11)]]
230  xticks[0][-1] = ""
231  xticks[1][-1] = ""
232  for j in range(0, 4):
233  axes[1, j].set_xticklabels(xticks[j%2])
234  for t in axes[1, j].get_xticklabels():
235  t.set_fontsize(11)
236  fig.legend(handles, ["exp/dev", "lux/luv", "approx exp/dev", "approx lux/luv"],
237  loc='lower center', ncol=4)
238  fig.text(centers[0], 0.95, "exponential", ha='center', weight='bold')
239  fig.text(centers[1], 0.95, "de Vaucouleur", ha='center', weight='bold')
240  return fig, axes
def evaluateRadial(basis, r, sbNormalize=False, doComponents=False)
Definition: tractor.py:92
def plotSuite(doComponents=False)
Definition: tractor.py:155
Provides consistent interface for LSST exceptions.
Definition: Exception.h:107
def registerRadialProfiles()
Definition: tractor.py:47
An ellipse defined by an arbitrary BaseCore and a center point.
Definition: Ellipse.h:51
An ellipse core for the semimajor/semiminor axis and position angle parametrization (a...
Definition: Axes.h:47
def integrateNormalizedFluxes(maxRadius=20.0, nSteps=5000)
Definition: tractor.py:122