23 Code to load multi-Gaussian approximations to profiles from "The Tractor"
24 into a lsst.shapelet.MultiShapeletBasis.
26 Please see the README file in the data directory of the lsst.shapelet
27 package for more information.
29 from future
import standard_library
30 standard_library.install_aliases()
31 from builtins
import zip
32 from builtins
import str
33 from builtins
import range
42 import lsst.pex.exceptions
43 from .radialProfile
import RadialProfile
44 from .multiShapeletBasis
import MultiShapeletBasis
45 from .shapeletFunction
import ShapeletFunction
48 """Register the pickled profiles in the data directory with the RadialProfile singleton registry.
50 This should only be called at import time by this module; it's only a function to avoid polluting
51 the module namespace with all the local variables used here.
53 dataDir = os.path.join(os.environ[
"SHAPELET_DIR"],
"data")
54 regex = re.compile(
r"([a-z]+\d?)_K(\d+)_MR(\d+)\.pickle")
55 for filename
in os.listdir(dataDir):
56 match = regex.match(filename)
60 nComponents = int(match.group(2))
61 maxRadius = int(match.group(3))
63 profile = RadialProfile.get(name)
64 except lsst.pex.exceptions.Exception:
65 warnings.warn(
"No C++ profile for multi-Gaussian pickle file '%s'" % filename)
67 with open(os.path.join(dataDir, filename),
'rb')
as stream:
68 if sys.version_info[0] >= 3:
69 array = pickle.load(stream, encoding=
'latin1')
71 array = pickle.load(stream)
72 amplitudes = array[:nComponents]
73 amplitudes /= amplitudes.sum()
74 variances = array[nComponents:]
75 if amplitudes.shape != (nComponents,)
or variances.shape != (nComponents,):
76 warnings.warn(
"Unknown format for multi-Gaussian pickle file '%s'" % filename)
78 basis = MultiShapeletBasis(1)
79 for amplitude, variance
in zip(amplitudes, variances):
80 radius = variance**0.5
81 matrix = numpy.array([[amplitude / ShapeletFunction.FLUX_FACTOR]], dtype=float)
82 basis.addComponent(radius, 0, matrix)
83 profile.registerBasis(basis, nComponents, maxRadius)
90 """Plot a single-element MultiShapeletBasis as a radial profile.
92 ellipse = lsst.afw.geom.ellipses.Ellipse(lsst.afw.geom.ellipses.Axes())
93 coefficients = numpy.ones(1, dtype=float)
94 msf = basis.makeFunction(ellipse, coefficients)
98 n += len(msf.getComponents())
99 z = numpy.zeros((n,) + r.shape, dtype=float)
100 for j, x
in enumerate(r):
103 for i, sf
in enumerate(msf.getComponents()):
105 for j, x
in enumerate(r):
106 z[i+1, j] = evc(x, 0.0)
114 After normalizing by surface brightness at r=1 r_e, integrate the profiles to compare
115 relative fluxes between the true profiles and their approximations.
117 @param[in] maxRadius Maximum radius to integrate the profile, in units of r_e.
118 @param[in] nSteps Number of concrete points at which to evaluate the profile to
119 do the integration (we just use the trapezoidal rule).
121 radii = numpy.linspace(0.0, maxRadius, nSteps)
122 profiles = {name: RadialProfile.get(name)
for name
in (
"exp",
"lux",
"dev",
"luv",
123 "ser2",
"ser3",
"ser5")}
125 for name, profile
in profiles.items():
126 evaluated[name] = profile.evaluate(radii)
127 basis = profile.getBasis(8)
128 evaluated[
"g" + name] =
evaluateRadial(basis, radii, sbNormalize=
True, doComponents=
False)[0, :]
129 fluxes = {name: numpy.trapz(z*radii, radii)
for name, z
in evaluated.items()}
134 """Plot all the profiles defined in this module together: true exp and dev, the SDSS softended/truncated
135 lux and luv, and the multi-Gaussian approximations to all of these.
137 To plot the individual Gaussians that form the multi-Gaussian approximations, pass doComponents=True.
139 Returns a tuple of (figure, axes), where 'figure' is the matplotlib figure that contains the plot,
140 and axes is a 2x4 NumPy array of matplotlib axes objects
142 from matplotlib
import pyplot
143 fig = pyplot.figure(figsize=(9, 4.7))
144 axes = numpy.zeros((2, 4), dtype=object)
145 r1 = numpy.logspace(-3, 0, 1000, base=10)
146 r2 = numpy.linspace(1, 10, 1000)
150 axes[i, j] = fig.add_subplot(2, 4, i*4+j+1)
151 profiles = {name: RadialProfile.get(name)
for name
in (
"exp",
"lux",
"dev",
"luv")}
152 basis = {name: profiles[name].getBasis(8)
for name
in profiles}
153 z = numpy.zeros((2, 4), dtype=object)
154 colors = (
"k",
"g",
"b",
"r")
155 fig.subplots_adjust(wspace=0.025, hspace=0.025, bottom=0.15, left=0.1, right=0.98, top=0.92)
156 centers = [None,
None]
158 for j
in range(0, 4, 2):
159 bbox0 = axes[i, j].get_position()
160 bbox1 = axes[i, j+1].get_position()
161 bbox1.x0 = bbox0.x1 - 0.06
163 centers[j/2] = 0.5*(bbox0.x0 + bbox1.x1)
164 axes[i, j].set_position(bbox0)
165 axes[i, j+1].set_position(bbox1)
166 for j
in range(0, 2):
167 z[0, j] = [
evaluateRadial(basis[k], r[j], sbNormalize=
True, doComponents=doComponents)
168 for k
in (
"exp",
"lux")]
169 z[0, j][0:0] = [profiles[k].evaluate(r[j])[numpy.newaxis, :]
for k
in (
"exp",
"lux")]
170 z[0, j+2] = [
evaluateRadial(basis[k], r[j], sbNormalize=
True, doComponents=doComponents)
171 for k
in (
"dev",
"luv")]
172 z[0, j+2][0:0] = [profiles[k].evaluate(r[j])[numpy.newaxis, :]
for k
in (
"dev",
"luv")]
173 methodNames = [[
"loglog",
"semilogy"], [
"semilogx",
"plot"]]
174 for j
in range(0, 4):
175 y[1, j] = [(y[0, j][0][0, :] - y[0, j][i][0, :])/y[0, j][0][0, :]
for i
in range(0, 4)]
177 method0 = getattr(axes[0, j], methodNames[0][j%2])
178 method1 = getattr(axes[1, j], methodNames[1][j%2])
181 handles.append(method0(r[j%2], y0[0, :], color=colors[k])[0])
183 for l
in range(1, y0.shape[0]):
184 method0(r[j%2], y0[l, :], color=colors[k], alpha=0.25)
185 method1(r[j%2], y[1, j][k], color=colors[k])
186 axes[0, j].set_xticklabels([])
187 axes[0, j].set_ylim(1E-6, 1E3)
188 axes[1, j].set_ylim(-0.2, 1.0)
189 for i, label
in enumerate((
"profile",
"relative error")):
190 axes[i, 0].set_ylabel(label)
191 for t
in axes[i, 0].get_yticklabels():
193 for j
in range(1, 4):
194 axes[0, j].set_yticklabels([])
195 axes[1, j].set_yticklabels([])
196 xticks = [[
'$\\mathdefault{10^{%d}}$' % i
for i
in range(-3, 1)],
197 [str(i)
for i
in range(1, 11)]]
200 for j
in range(0, 4):
201 axes[1, j].set_xticklabels(xticks[j%2])
202 for t
in axes[1, j].get_xticklabels():
204 fig.legend(handles, [
"exp/dev",
"lux/luv",
"approx exp/dev",
"approx lux/luv"],
205 loc=
'lower center', ncol=4)
206 fig.text(centers[0], 0.95,
"exponential", ha=
'center', weight=
'bold')
207 fig.text(centers[1], 0.95,
"de Vaucouleur", ha=
'center', weight=
'bold')
def registerRadialProfiles
def integrateNormalizedFluxes
After normalizing by surface brightness at r=1 r_e, integrate the profiles to compare relative fluxes...