23 __all__ = [
'PlotPhotonTransferCurveTask']
26 import matplotlib.pyplot
as plt
27 import matplotlib
as mpl
28 from matplotlib
import gridspec
30 from matplotlib.backends.backend_pdf
import PdfPages
35 calculateWeightedReducedChi2)
36 from matplotlib.ticker
import MaxNLocator
43 """A class to plot the dataset from MeasurePhotonTransferCurveTask.
48 datasetFileName : `str`
49 datasetPtc (lsst.ip.isr.PhotonTransferCurveDataset) file
52 linearizerFileName : `str`, optional
53 linearizer (isr.linearize.Linearizer) file
56 outDir : `str`, optional
57 Path to the output directory where the final PDF will
60 detNum : `int`, optional
63 signalElectronsRelativeA : `float`, optional
64 Signal value for relative systematic bias between different
65 methods of estimating a_ij (Fig. 15 of Astier+19).
67 plotNormalizedCovariancesNumberOfBins : `float`, optional
68 Number of bins in `plotNormalizedCovariancesNumber` function
69 (Fig. 8, 10., of Astier+19).
73 The plotting code in this file is almost identical to the code in
74 `plotPtcGen2.py`. If further changes are implemented in this file,
75 `plotPtcGen2.py` needs to be updated accordingly, and vice versa.
76 The file `plotPtcGen2.py` helps with maintaining backwards
77 compatibility with gen2 as we transition to gen3; the code
78 duplication is meant to only last for few month from now
79 (Jan, 2021). At that point only this file, `plotPtc.py`, will
83 def __init__(self, datasetFilename, linearizerFileName=None,
84 outDir='.', detNum=999, signalElectronsRelativeA=75000,
85 plotNormalizedCovariancesNumberOfBins=10):
94 """Run the Photon Transfer Curve (PTC) plotting measurement task.
98 datasetPtc = PhotonTransferCurveDataset.readFits(datasetFile)
100 dirname = self.
outDiroutDir
101 if not os.path.exists(dirname):
104 detNum = self.
detNumdetNum
105 filename = f
"PTC_det{detNum}.pdf"
106 filenameFull = os.path.join(dirname, filename)
109 linearizer = isr.linearize.Linearizer.readFits(self.
linearizerFileNamelinearizerFileName)
112 self.
runrun(filenameFull, datasetPtc, linearizer=linearizer, log=lsstLog)
116 def run(self, filenameFull, datasetPtc, linearizer=None, log=None):
117 """Make the plots for the PTC task"""
118 ptcFitType = datasetPtc.ptcFitType
119 with PdfPages(filenameFull)
as pdfPages:
120 if ptcFitType
in [
"FULLCOVARIANCE", ]:
122 elif ptcFitType
in [
"EXPAPPROXIMATION",
"POLYNOMIAL"]:
125 raise RuntimeError(f
"The input dataset had an invalid dataset.ptcFitType: {ptcFitType}. \n"
126 "Options: 'FULLCOVARIANCE', EXPAPPROXIMATION, or 'POLYNOMIAL'.")
134 """Make plots for MeasurePhotonTransferCurve task when doCovariancesAstier=True.
136 This function call other functions that mostly reproduce the plots in Astier+19.
137 Most of the code is ported from Pierre Astier's repository https://github.com/PierreAstier/bfptc
141 dataset : `lsst.ip.isr.ptcDataset.PhotonTransferCurveDataset`
142 The dataset containing the necessary information to produce the plots.
144 pdfPages: `matplotlib.backends.backend_pdf.PdfPages`
145 PDF file where the plots will be saved.
147 log : `lsst.log.Log`, optional
148 Logger to handle messages
150 mu = dataset.finalMeans
152 fullCovs = dataset.covariances
153 fullCovsModel = dataset.covariancesModel
154 fullCovWeights = dataset.covariancesSqrtWeights
155 aDict = dataset.aMatrix
156 bDict = dataset.bMatrix
157 fullCovsNoB = dataset.covariances
158 fullCovsModelNoB = dataset.covariancesModelNoB
159 fullCovWeightsNoB = dataset.covariancesSqrtWeights
160 aDictNoB = dataset.aMatrixNoB
161 gainDict = dataset.gain
162 noiseDict = dataset.noise
164 self.
plotCovariancesplotCovariances(mu, fullCovs, fullCovsModel, fullCovWeights, fullCovsNoB, fullCovsModelNoB,
165 fullCovWeightsNoB, gainDict, noiseDict, aDict, bDict, pdfPages)
167 fullCovsModelNoB, fullCovWeightsNoB, pdfPages,
168 offset=0.01, topPlot=
True,
172 fullCovsModelNoB, fullCovWeightsNoB, pdfPages,
176 fullCovsModelNoB, fullCovWeightsNoB, pdfPages,
179 self.
plot_a_bplot_a_b(aDict, bDict, pdfPages)
180 self.
ab_vs_distab_vs_dist(aDict, bDict, pdfPages, bRange=4)
188 def plotCovariances(mu, covs, covsModel, covsWeights, covsNoB, covsModelNoB, covsWeightsNoB,
189 gainDict, noiseDict, aDict, bDict, pdfPages):
190 """Plot covariances and models: Cov00, Cov10, Cov01.
192 Figs. 6 and 7 of Astier+19
196 mu : `dict`, [`str`, `list`]
197 Dictionary keyed by amp name with mean signal values.
199 covs : `dict`, [`str`, `list`]
200 Dictionary keyed by amp names containing a list of measued covariances per mean flux.
202 covsModel : `dict`, [`str`, `list`]
203 Dictionary keyed by amp names containinging covariances model (Eq. 20 of Astier+19) per mean flux.
205 covsWeights : `dict`, [`str`, `list`]
206 Dictionary keyed by amp names containinging sqrt. of covariances weights.
208 covsNoB : `dict`, [`str`, `list`]
209 Dictionary keyed by amp names containing a list of measued covariances per mean flux ('b'=0 in
212 covsModelNoB : `dict`, [`str`, `list`]
213 Dictionary keyed by amp names containing covariances model (with 'b'=0 in Eq. 20 of Astier+19)
216 covsWeightsNoB : `dict`, [`str`, `list`]
217 Dictionary keyed by amp names containing sqrt. of covariances weights ('b' = 0 in Eq. 20 of
220 gainDict : `dict`, [`str`, `float`]
221 Dictionary keyed by amp names containing the gains in e-/ADU.
223 noiseDict : `dict`, [`str`, `float`]
224 Dictionary keyed by amp names containing the rms redout noise in e-.
226 aDict : `dict`, [`str`, `numpy.array`]
227 Dictionary keyed by amp names containing 'a' coefficients (Eq. 20 of Astier+19).
229 bDict : `dict`, [`str`, `numpy.array`]
230 Dictionary keyed by amp names containing 'b' coefficients (Eq. 20 of Astier+19).
232 pdfPages: `matplotlib.backends.backend_pdf.PdfPages`
233 PDF file where the plots will be saved.
239 supTitleFontSize = 18
245 nRows = np.sqrt(nAmps)
246 mantissa, _ = np.modf(nRows)
248 nRows = int(nRows) + 1
254 f, ax = plt.subplots(nrows=nRows, ncols=nCols, sharex=
'col', sharey=
'row', figsize=(13, 10))
255 f2, ax2 = plt.subplots(nrows=nRows, ncols=nCols, sharex=
'col', sharey=
'row', figsize=(13, 10))
256 fResCov00, axResCov00 = plt.subplots(nrows=nRows, ncols=nCols, sharex=
'col', sharey=
'row',
258 fCov01, axCov01 = plt.subplots(nrows=nRows, ncols=nCols, sharex=
'col', sharey=
'row', figsize=(13, 10))
259 fCov10, axCov10 = plt.subplots(nrows=nRows, ncols=nCols, sharex=
'col', sharey=
'row', figsize=(13, 10))
261 assert(len(covsModel) == nAmps)
262 assert(len(covsWeights) == nAmps)
264 assert(len(covsNoB) == nAmps)
265 assert(len(covsModelNoB) == nAmps)
266 assert(len(covsWeightsNoB) == nAmps)
268 for i, (amp, a, a2, aResVar, a3, a4)
in enumerate(zip(covs, ax.flatten(),
269 ax2.flatten(), axResCov00.flatten(),
270 axCov01.flatten(), axCov10.flatten())):
272 muAmp, cov, model, weight = mu[amp], covs[amp], covsModel[amp], covsWeights[amp]
273 if not np.isnan(np.array(cov)).
all():
274 aCoeffs, bCoeffs = np.array(aDict[amp]), np.array(bDict[amp])
275 gain, noise = gainDict[amp], noiseDict[amp]
276 (meanVecFinal, varVecFinal, varVecModelFinal,
282 varWeightsFinal, len(meanVecFinal), 4)
284 (meanVecFinalCov01, varVecFinalCov01, varVecModelFinalCov01,
287 (meanVecFinalCov10, varVecFinalCov10, varVecModelFinalCov10,
291 par2 = np.polyfit(meanVecFinal, varVecFinal, 2, w=varWeightsFinal)
292 varModelFinalQuadratic = np.polyval(par2, meanVecFinal)
294 varWeightsFinal, len(meanVecFinal), 3)
297 covNoB, modelNoB, weightNoB = covsNoB[amp], covsModelNoB[amp], covsWeightsNoB[amp]
298 (meanVecFinalNoB, varVecFinalNoB, varVecModelFinalNoB,
300 weightNoB, returnMasked=
True)
303 varWeightsFinalNoB, len(meanVecFinalNoB),
305 stringLegend = (f
"Gain: {gain:.4} e/ADU \n"
306 f
"Noise: {noise:.4} e \n"
307 +
r"$a_{00}$: %.3e 1/e"%aCoeffs[0, 0] +
"\n"
308 +
r"$b_{00}$: %.3e 1/e"%bCoeffs[0, 0]
309 + f
"\nLast in fit: {meanVecFinal[-1]:.7} ADU ")
310 minMeanVecFinal = np.nanmin(meanVecFinal)
311 maxMeanVecFinal = np.nanmax(meanVecFinal)
312 deltaXlim = maxMeanVecFinal - minMeanVecFinal
314 a.set_xlabel(
r'Mean signal ($\mu$, ADU)', fontsize=labelFontSize)
315 a.set_ylabel(
r'Variance (ADU$^2$)', fontsize=labelFontSize)
316 a.tick_params(labelsize=11)
317 a.set_xscale(
'linear')
318 a.set_yscale(
'linear')
319 a.scatter(meanVecFinal, varVecFinal, c=
'blue', marker=
'o', s=markerSize)
320 a.plot(meanVecFinal, varVecModelFinal, color=
'red', lineStyle=
'-')
321 a.text(0.03, 0.7, stringLegend, transform=a.transAxes, fontsize=legendFontSize)
322 a.set_title(amp, fontsize=titleFontSize)
323 a.set_xlim([minMeanVecFinal - 0.2*deltaXlim, maxMeanVecFinal + 0.2*deltaXlim])
326 a2.set_xlabel(
r'Mean Signal ($\mu$, ADU)', fontsize=labelFontSize)
327 a2.set_ylabel(
r'Variance (ADU$^2$)', fontsize=labelFontSize)
328 a2.tick_params(labelsize=11)
331 a2.plot(meanVecFinal, varVecModelFinal, color=
'red', lineStyle=
'-')
332 a2.scatter(meanVecFinal, varVecFinal, c=
'blue', marker=
'o', s=markerSize)
333 a2.text(0.03, 0.7, stringLegend, transform=a2.transAxes, fontsize=legendFontSize)
334 a2.set_title(amp, fontsize=titleFontSize)
335 a2.set_xlim([minMeanVecFinal, maxMeanVecFinal])
338 aResVar.set_xlabel(
r'Mean signal ($\mu$, ADU)', fontsize=labelFontSize)
339 aResVar.set_ylabel(
r'Residuals (ADU$^2$)', fontsize=labelFontSize)
340 aResVar.tick_params(labelsize=11)
341 aResVar.set_xscale(
'linear')
342 aResVar.set_yscale(
'linear')
343 aResVar.plot(meanVecFinal, varVecFinal - varVecModelFinal, color=
'blue', lineStyle=
'-',
344 label=
r'Full fit ($\chi_{\rm{red}}^2$: %g)'%chi2FullModelVar)
345 aResVar.plot(meanVecFinal, varVecFinal - varModelFinalQuadratic, color=
'red', lineStyle=
'-',
346 label=
r'Quadratic fit ($\chi_{\rm{red}}^2$: %g)'%chi2QuadModelVar)
347 aResVar.plot(meanVecFinalNoB, varVecFinalNoB - varVecModelFinalNoB, color=
'green',
349 label=
r'Full fit (b=0) ($\chi_{\rm{red}}^2$: %g)'%chi2FullModelNoBVar)
350 aResVar.axhline(color=
'black')
351 aResVar.set_title(amp, fontsize=titleFontSize)
352 aResVar.set_xlim([minMeanVecFinal - 0.2*deltaXlim, maxMeanVecFinal + 0.2*deltaXlim])
353 aResVar.legend(fontsize=7)
355 a3.set_xlabel(
r'Mean signal ($\mu$, ADU)', fontsize=labelFontSize)
356 a3.set_ylabel(
r'Cov01 (ADU$^2$)', fontsize=labelFontSize)
357 a3.tick_params(labelsize=11)
358 a3.set_xscale(
'linear')
359 a3.set_yscale(
'linear')
360 a3.scatter(meanVecFinalCov01, varVecFinalCov01, c=
'blue', marker=
'o', s=markerSize)
361 a3.plot(meanVecFinalCov01, varVecModelFinalCov01, color=
'red', lineStyle=
'-')
362 a3.set_title(amp, fontsize=titleFontSize)
363 a3.set_xlim([minMeanVecFinal - 0.2*deltaXlim, maxMeanVecFinal + 0.2*deltaXlim])
365 a4.set_xlabel(
r'Mean signal ($\mu$, ADU)', fontsize=labelFontSize)
366 a4.set_ylabel(
r'Cov10 (ADU$^2$)', fontsize=labelFontSize)
367 a4.tick_params(labelsize=11)
368 a4.set_xscale(
'linear')
369 a4.set_yscale(
'linear')
370 a4.scatter(meanVecFinalCov10, varVecFinalCov10, c=
'blue', marker=
'o', s=markerSize)
371 a4.plot(meanVecFinalCov10, varVecModelFinalCov10, color=
'red', lineStyle=
'-')
372 a4.set_title(amp, fontsize=titleFontSize)
373 a4.set_xlim([minMeanVecFinal - 0.2*deltaXlim, maxMeanVecFinal + 0.2*deltaXlim])
376 a.set_title(f
"{amp} (BAD)", fontsize=titleFontSize)
377 a2.set_title(f
"{amp} (BAD)", fontsize=titleFontSize)
378 a3.set_title(f
"{amp} (BAD)", fontsize=titleFontSize)
379 a4.set_title(f
"{amp} (BAD)", fontsize=titleFontSize)
381 f.suptitle(
"PTC from covariances as in Astier+19 \n Fit: Eq. 20, Astier+19",
382 fontsize=supTitleFontSize)
384 f2.suptitle(
"PTC from covariances as in Astier+19 (log-log) \n Fit: Eq. 20, Astier+19",
385 fontsize=supTitleFontSize)
387 fResCov00.suptitle(
"Residuals (data-model) for Cov00 (Var)", fontsize=supTitleFontSize)
388 pdfPages.savefig(fResCov00)
389 fCov01.suptitle(
"Cov01 as in Astier+19 (nearest parallel neighbor covariance) \n"
390 " Fit: Eq. 20, Astier+19", fontsize=supTitleFontSize)
391 pdfPages.savefig(fCov01)
392 fCov10.suptitle(
"Cov10 as in Astier+19 (nearest serial neighbor covariance) \n"
393 "Fit: Eq. 20, Astier+19", fontsize=supTitleFontSize)
394 pdfPages.savefig(fCov10)
399 covsWeightsNoB, pdfPages, offset=0.004,
400 numberOfBins=10, plotData=True, topPlot=False, log=None):
401 """Plot C_ij/mu vs mu.
403 Figs. 8, 10, and 11 of Astier+19
413 inputMu : `dict`, [`str`, `list`]
414 Dictionary keyed by amp name with mean signal values.
416 covs : `dict`, [`str`, `list`]
417 Dictionary keyed by amp names containing a list of measued covariances per mean flux.
419 covsModel : `dict`, [`str`, `list`]
420 Dictionary keyed by amp names containinging covariances model (Eq. 20 of Astier+19) per mean flux.
422 covsWeights : `dict`, [`str`, `list`]
423 Dictionary keyed by amp names containinging sqrt. of covariances weights.
425 covsNoB : `dict`, [`str`, `list`]
426 Dictionary keyed by amp names containing a list of measued covariances per mean flux ('b'=0 in
429 covsModelNoB : `dict`, [`str`, `list`]
430 Dictionary keyed by amp names containing covariances model (with 'b'=0 in Eq. 20 of Astier+19)
433 covsWeightsNoB : `dict`, [`str`, `list`]
434 Dictionary keyed by amp names containing sqrt. of covariances weights ('b' = 0 in Eq. 20 of
437 expIdMask : `dict`, [`str`, `list`]
438 Dictionary keyed by amp names containing the masked exposure pairs.
440 pdfPages: `matplotlib.backends.backend_pdf.PdfPages`
441 PDF file where the plots will be saved.
443 offset : `float`, optional
444 Constant offset factor to plot covariances in same panel (so they don't overlap).
446 numberOfBins : `int`, optional
447 Number of bins for top and bottom plot.
449 plotData : `bool`, optional
450 Plot the data points?
452 topPlot : `bool`, optional
453 Plot the top plot with the covariances, and the bottom plot with the model residuals?
455 log : `lsst.log.Log`, optional
456 Logger to handle messages.
459 fig = plt.figure(figsize=(8, 10))
460 gs = gridspec.GridSpec(2, 1, height_ratios=[3, 1])
462 ax0 = plt.subplot(gs[0])
463 plt.setp(ax0.get_xticklabels(), visible=
False)
465 fig = plt.figure(figsize=(8, 8))
466 ax0 = plt.subplot(111)
467 ax0.ticklabel_format(style=
'sci', axis=
'x', scilimits=(0, 0))
468 ax0.tick_params(axis=
'both', labelsize=
'x-large')
469 mue, rese, wce = [], [], []
470 mueNoB, reseNoB, wceNoB = [], [], []
471 for counter, amp
in enumerate(covs):
472 muAmp, fullCov, fullCovModel, fullCovWeight = (inputMu[amp], covs[amp], covsModel[amp],
474 if len(fullCov) == 0:
477 fullCovWeight, divideByMu=
True,
481 rese +=
list(cov - model)
482 wce +=
list(weightCov)
484 fullCovNoB, fullCovModelNoB, fullCovWeightNoB = (covsNoB[amp], covsModelNoB[amp],
486 if len(fullCovNoB) == 0:
488 (muNoB, covNoB, modelNoB,
490 fullCovWeightNoB, divideByMu=
True,
493 mueNoB +=
list(muNoB)
494 reseNoB +=
list(covNoB - modelNoB)
495 wceNoB +=
list(weightCovNoB)
498 fit_curve, = plt.plot(mu, model + counter*offset,
'-', linewidth=4.0)
502 xb, yb, wyb, sigyb = self.
binDatabinData(mu, cov, gind, weightCov)
503 plt.errorbar(xb, yb+counter*offset, yerr=sigyb, marker=
'o', linestyle=
'none', markersize=6.5,
504 color=fit_curve.get_color(), label=f
"{amp} (N: {len(mu)})")
507 points, = plt.plot(mu, cov + counter*offset,
'.', color=fit_curve.get_color())
508 plt.legend(loc=
'upper right', fontsize=8)
511 rese = np.array(rese)
513 mueNoB = np.array(mueNoB)
514 reseNoB = np.array(reseNoB)
515 wceNoB = np.array(wceNoB)
517 plt.xlabel(
r"$\mu (el)$", fontsize=
'x-large')
518 plt.ylabel(
r"$Cov{%d%d}/\mu + Cst (el)$"%(i, j), fontsize=
'x-large')
521 xb, yb, wyb, sigyb = self.
binDatabinData(mue, rese, gind, wce)
523 ax1 = plt.subplot(gs[1], sharex=ax0)
524 ax1.errorbar(xb, yb, yerr=sigyb, marker=
'o', linestyle=
'none', label=
'Full fit')
525 gindNoB = self.
indexForBinsindexForBins(mueNoB, numberOfBins)
526 xb2, yb2, wyb2, sigyb2 = self.
binDatabinData(mueNoB, reseNoB, gindNoB, wceNoB)
528 ax1.errorbar(xb2, yb2, yerr=sigyb2, marker=
'o', linestyle=
'none', label=
'b = 0')
529 ax1.tick_params(axis=
'both', labelsize=
'x-large')
530 plt.legend(loc=
'upper left', fontsize=
'large')
532 plt.plot(xb, [0]*len(xb),
'--', color=
'k')
533 plt.ticklabel_format(style=
'sci', axis=
'x', scilimits=(0, 0))
534 plt.ticklabel_format(style=
'sci', axis=
'y', scilimits=(0, 0))
535 plt.xlabel(
r'$\mu (el)$', fontsize=
'x-large')
536 plt.ylabel(
r'$Cov{%d%d}/\mu$ -model (el)'%(i, j), fontsize=
'x-large')
538 plt.suptitle(f
"Nbins: {numberOfBins}")
541 labels0 = [item.get_text()
for item
in ax0.get_yticklabels()]
543 ax0.set_yticklabels(labels0)
544 pdfPages.savefig(fig)
550 """Fig. 12 of Astier+19
552 Color display of a and b arrays fits, averaged over channels.
556 aDict : `dict`, [`numpy.array`]
557 Dictionary keyed by amp names containing the fitted 'a' coefficients from the model
558 in Eq. 20 of Astier+19 (if `ptcFitType` is `FULLCOVARIANCE`).
560 bDict : `dict`, [`numpy.array`]
561 Dictionary keyed by amp names containing the fitted 'b' coefficients from the model
562 in Eq. 20 of Astier+19 (if `ptcFitType` is `FULLCOVARIANCE`).
564 pdfPages: `matplotlib.backends.backend_pdf.PdfPages`
565 PDF file where the plots will be saved.
568 Maximum lag for b arrays.
572 if np.isnan(aDict[amp]).
all():
576 a = np.array(a).mean(axis=0)
577 b = np.array(b).mean(axis=0)
578 fig = plt.figure(figsize=(7, 11))
579 ax0 = fig.add_subplot(2, 1, 1)
580 im0 = ax0.imshow(np.abs(a.transpose()), origin=
'lower', norm=mpl.colors.LogNorm())
581 ax0.tick_params(axis=
'both', labelsize=
'x-large')
582 ax0.set_title(
r'$|a|$', fontsize=
'x-large')
583 ax0.xaxis.set_ticks_position(
'bottom')
584 cb0 = plt.colorbar(im0)
585 cb0.ax.tick_params(labelsize=
'x-large')
587 ax1 = fig.add_subplot(2, 1, 2)
588 ax1.tick_params(axis=
'both', labelsize=
'x-large')
589 ax1.yaxis.set_major_locator(MaxNLocator(integer=
True))
590 ax1.xaxis.set_major_locator(MaxNLocator(integer=
True))
591 im1 = ax1.imshow(1e6*b[:bRange, :bRange].transpose(), origin=
'lower')
592 cb1 = plt.colorbar(im1)
593 cb1.ax.tick_params(labelsize=
'x-large')
594 ax1.set_title(
r'$b \times 10^6$', fontsize=
'x-large')
595 ax1.xaxis.set_ticks_position(
'bottom')
597 pdfPages.savefig(fig)
603 """Fig. 13 of Astier+19.
605 Values of a and b arrays fits, averaged over amplifiers, as a function of distance.
609 aDict : `dict`, [`numpy.array`]
610 Dictionary keyed by amp names containing the fitted 'a' coefficients from the model
611 in Eq. 20 of Astier+19 (if `ptcFitType` is `FULLCOVARIANCE`).
613 bDict : `dict`, [`numpy.array`]
614 Dictionary keyed by amp names containing the fitted 'b' coefficients from the model
615 in Eq. 20 of Astier+19 (if `ptcFitType` is `FULLCOVARIANCE`).
617 pdfPages: `matplotlib.backends.backend_pdf.PdfPages`
618 PDF file where the plots will be saved.
621 Maximum lag for b arrays.
623 assert (len(aDict) == len(bDict))
626 if np.isnan(aDict[amp]).
all():
631 sy = a.std(axis=0)/np.sqrt(len(aDict))
632 i, j = np.indices(y.shape)
633 upper = (i >= j).ravel()
634 r = np.sqrt(i**2 + j**2).ravel()
637 fig = plt.figure(figsize=(6, 9))
638 ax = fig.add_subplot(211)
639 ax.set_xlim([0.5, r.max()+1])
640 ax.errorbar(r[upper], y[upper], yerr=sy[upper], marker=
'o', linestyle=
'none', color=
'b',
642 ax.errorbar(r[~upper], y[~upper], yerr=sy[~upper], marker=
'o', linestyle=
'none', color=
'r',
644 ax.legend(loc=
'upper center', fontsize=
'x-large')
645 ax.set_xlabel(
r'$\sqrt{i^2+j^2}$', fontsize=
'x-large')
646 ax.set_ylabel(
r'$a_{ij}$', fontsize=
'x-large')
648 ax.tick_params(axis=
'both', labelsize=
'x-large')
651 axb = fig.add_subplot(212)
654 if np.isnan(bDict[amp]).
all():
659 syb = b.std(axis=0)/np.sqrt(len(bDict))
660 ib, jb = np.indices(yb.shape)
661 upper = (ib > jb).ravel()
662 rb = np.sqrt(i**2 + j**2).ravel()
667 axb.set_xlim([xmin, xmax+0.2])
668 cutu = (r > xmin) & (r < xmax) & (upper)
669 cutl = (r > xmin) & (r < xmax) & (~upper)
670 axb.errorbar(rb[cutu], yb[cutu], yerr=syb[cutu], marker=
'o', linestyle=
'none', color=
'b',
672 axb.errorbar(rb[cutl], yb[cutl], yerr=syb[cutl], marker=
'o', linestyle=
'none', color=
'r',
674 plt.legend(loc=
'upper center', fontsize=
'x-large')
675 axb.set_xlabel(
r'$\sqrt{i^2+j^2}$', fontsize=
'x-large')
676 axb.set_ylabel(
r'$b_{ij}$', fontsize=
'x-large')
677 axb.ticklabel_format(style=
'sci', axis=
'y', scilimits=(0, 0))
678 axb.tick_params(axis=
'both', labelsize=
'x-large')
680 pdfPages.savefig(fig)
686 """Fig. 14. of Astier+19
688 Cumulative sum of a_ij as a function of maximum separation. This plot displays the average over
693 aDict : `dict`, [`numpy.array`]
694 Dictionary keyed by amp names containing the fitted 'a' coefficients from the model
695 in Eq. 20 of Astier+19 (if `ptcFitType` is `FULLCOVARIANCE`).
697 bDict : `dict`, [`numpy.array`]
698 Dictionary keyed by amp names containing the fitted 'b' coefficients from the model
699 in Eq. 20 of Astier+19 (if `ptcFitType` is `FULLCOVARIANCE`).
701 pdfPages: `matplotlib.backends.backend_pdf.PdfPages`
702 PDF file where the plots will be saved.
704 assert (len(aDict) == len(bDict))
707 if np.isnan(aDict[amp]).
all()
or np.isnan(bDict[amp]).
all():
711 a = np.array(a).mean(axis=0)
712 b = np.array(b).mean(axis=0)
713 fig = plt.figure(figsize=(7, 6))
714 w = 4*np.ones_like(a)
719 indices = range(1, a.shape[0]+1)
720 sums = [wa[0:n, 0:n].sum()
for n
in indices]
721 ax = plt.subplot(111)
722 ax.plot(indices, sums/sums[0],
'o', color=
'b')
724 ax.set_xlim(indices[0]-0.5, indices[-1]+0.5)
725 ax.set_ylim(
None, 1.2)
726 ax.set_ylabel(
r'$[\sum_{|i|<n\ &\ |j|<n} a_{ij}] / |a_{00}|$', fontsize=
'x-large')
727 ax.set_xlabel(
'n', fontsize=
'x-large')
728 ax.tick_params(axis=
'both', labelsize=
'x-large')
730 pdfPages.savefig(fig)
736 gainDict, pdfPages, maxr=None):
737 """Fig. 15 in Astier+19.
739 Illustrates systematic bias from estimating 'a'
740 coefficients from the slope of correlations as opposed to the
741 full model in Astier+19.
746 Dictionary of 'a' matrices (Eq. 20, Astier+19), with amp names as keys.
749 Dictionary of 'a' matrices ('b'= 0 in Eq. 20, Astier+19), with amp names as keys.
751 fullCovsModel : `dict`, [`str`, `list`]
752 Dictionary keyed by amp names containing covariances model per mean flux.
754 fullCovsModelNoB : `dict`, [`str`, `list`]
755 Dictionary keyed by amp names containing covariances model (with 'b'=0 in Eq. 20 of
756 Astier+19) per mean flux.
758 signalElectrons : `float`
759 Signal at which to evaluate the a_ij coefficients.
761 pdfPages: `matplotlib.backends.backend_pdf.PdfPages`
762 PDF file where the plots will be saved.
764 gainDict : `dict`, [`str`, `float`]
765 Dicgionary keyed by amp names with the gains in e-/ADU.
767 maxr : `int`, optional
771 fig = plt.figure(figsize=(7, 11))
772 title = [f
"'a' relative bias at {signalElectrons} e",
"'a' relative bias (b=0)"]
773 data = [(aDict, fullCovsModel), (aDictNoB, fullCovsModelNoB)]
775 for k, pair
in enumerate(data):
779 covModel = np.array(pair[1][amp])
780 if np.isnan(covModel).
all():
786 var = covModel[0, 0, 0]
788 aOld = covModel[0, :, :]/(var*signalElectrons)
791 diffs.append((aOld-a))
792 amean = np.array(amean).mean(axis=0)
793 diff = np.array(diffs).mean(axis=0)
800 diff = diff[:maxr, :maxr]
801 ax0 = fig.add_subplot(2, 1, k+1)
802 im0 = ax0.imshow(diff.transpose(), origin=
'lower')
803 ax0.yaxis.set_major_locator(MaxNLocator(integer=
True))
804 ax0.xaxis.set_major_locator(MaxNLocator(integer=
True))
805 ax0.tick_params(axis=
'both', labelsize=
'x-large')
807 ax0.set_title(title[k])
810 pdfPages.savefig(fig)
814 def _plotStandardPtc(self, dataset, ptcFitType, pdfPages):
815 """Plot PTC, var/signal vs signal, linearity, and linearity residual per amplifier.
819 dataset : `lsst.ip.isr.ptcDataset.PhotonTransferCurveDataset`
820 The dataset containing the means, variances, exposure times, and mask.
823 Type of the model fit to the PTC. Options: 'FULLCOVARIANCE', EXPAPPROXIMATION, or 'POLYNOMIAL'.
825 pdfPages: `matplotlib.backends.backend_pdf.PdfPages`
826 PDF file where the plots will be saved.
829 if ptcFitType ==
'EXPAPPROXIMATION':
831 stringTitle = (
r"Var = $\frac{1}{2g^2a_{00}}(\exp (2a_{00} \mu g) - 1) + \frac{n_{00}}{g^2}$ ")
832 elif ptcFitType ==
'POLYNOMIAL':
833 ptcFunc = funcPolynomial
834 for key
in dataset.ptcFitPars:
835 deg = len(dataset.ptcFitPars[key]) - 1
837 stringTitle =
r"Polynomial (degree: %g)" % (deg)
839 raise RuntimeError(f
"The input dataset had an invalid dataset.ptcFitType: {ptcFitType}. \n"
840 "Options: 'FULLCOVARIANCE', EXPAPPROXIMATION, or 'POLYNOMIAL'.")
845 supTitleFontSize = 18
849 nAmps = len(dataset.ampNames)
852 nRows = np.sqrt(nAmps)
853 mantissa, _ = np.modf(nRows)
855 nRows = int(nRows) + 1
861 f, ax = plt.subplots(nrows=nRows, ncols=nCols, sharex=
'col', sharey=
'row', figsize=(13, 10))
862 f2, ax2 = plt.subplots(nrows=nRows, ncols=nCols, sharex=
'col', sharey=
'row', figsize=(13, 10))
863 f3, ax3 = plt.subplots(nrows=nRows, ncols=nCols, sharex=
'col', sharey=
'row', figsize=(13, 10))
865 for i, (amp, a, a2, a3)
in enumerate(zip(dataset.ampNames, ax.flatten(), ax2.flatten(),
867 meanVecOriginal = np.ravel(np.array(dataset.rawMeans[amp]))
868 varVecOriginal = np.ravel(np.array(dataset.rawVars[amp]))
869 mask = np.ravel(np.array(dataset.expIdMask[amp]))
870 if np.isnan(mask[0]):
871 a.set_title(f
"{amp} (BAD)", fontsize=titleFontSize)
872 a2.set_title(f
"{amp} (BAD)", fontsize=titleFontSize)
873 a3.set_title(f
"{amp} (BAD)", fontsize=titleFontSize)
876 mask = mask.astype(bool)
877 meanVecFinal = meanVecOriginal[mask]
878 varVecFinal = varVecOriginal[mask]
879 meanVecOutliers = meanVecOriginal[np.invert(mask)]
880 varVecOutliers = varVecOriginal[np.invert(mask)]
881 pars, parsErr = np.array(dataset.ptcFitPars[amp]), np.array(dataset.ptcFitParsError[amp])
882 ptcRedChi2 = dataset.ptcFitChiSq[amp]
883 if ptcFitType ==
'EXPAPPROXIMATION':
884 if len(meanVecFinal):
885 ptcA00, ptcA00error = pars[0], parsErr[0]
886 ptcGain, ptcGainError = pars[1], parsErr[1]
887 ptcNoise = np.sqrt((pars[2]))
888 ptcNoiseAdu = ptcNoise*(1./ptcGain)
889 ptcNoiseError = 0.5*(parsErr[2]/np.fabs(pars[2]))*np.sqrt(np.fabs(pars[2]))
890 stringLegend = (f
"a00: {ptcA00:.2e}+/-{ptcA00error:.2e} 1/e"
891 f
"\nGain: {ptcGain:.4}+/-{ptcGainError:.2e} e/ADU"
892 f
"\nNoise: {ptcNoise:.4}+/-{ptcNoiseError:.2e} e\n"
893 r"$\chi^2_{\rm{red}}$: " + f
"{ptcRedChi2:.4}"
894 f
"\nLast in fit: {meanVecFinal[-1]:.7} ADU ")
896 if ptcFitType ==
'POLYNOMIAL':
897 if len(meanVecFinal):
898 ptcGain, ptcGainError = 1./pars[1], np.fabs(1./pars[1])*(parsErr[1]/pars[1])
899 ptcNoiseAdu = np.sqrt((pars[0]))
900 ptcNoise = ptcNoiseAdu*ptcGain
901 ptcNoiseError = (0.5*(parsErr[0]/np.fabs(pars[0]))*(np.sqrt(np.fabs(pars[0]))))*ptcGain
902 stringLegend = (f
"Gain: {ptcGain:.4}+/-{ptcGainError:.2e} e/ADU\n"
903 f
"Noise: {ptcNoise:.4}+/-{ptcNoiseError:.2e} e\n"
904 r"$\chi^2_{\rm{red}}$: " + f
"{ptcRedChi2:.4}"
905 f
"\nLast in fit: {meanVecFinal[-1]:.7} ADU ")
907 a.set_xlabel(
r'Mean signal ($\mu$, ADU)', fontsize=labelFontSize)
908 a.set_ylabel(
r'Variance (ADU$^2$)', fontsize=labelFontSize)
909 a.tick_params(labelsize=11)
910 a.set_xscale(
'linear')
911 a.set_yscale(
'linear')
913 a2.set_xlabel(
r'Mean Signal ($\mu$, ADU)', fontsize=labelFontSize)
914 a2.set_ylabel(
r'Variance (ADU$^2$)', fontsize=labelFontSize)
915 a2.tick_params(labelsize=11)
919 a3.set_xlabel(
r'Mean signal ($\mu$, ADU)', fontsize=labelFontSize)
920 a3.set_ylabel(
r'Variance/$\mu$ (ADU)', fontsize=labelFontSize)
921 a3.tick_params(labelsize=11)
923 a3.set_yscale(
'linear')
925 minMeanVecFinal = np.nanmin(meanVecFinal)
926 maxMeanVecFinal = np.nanmax(meanVecFinal)
927 meanVecFit = np.linspace(minMeanVecFinal, maxMeanVecFinal, 100*len(meanVecFinal))
928 minMeanVecOriginal = np.nanmin(meanVecOriginal)
929 maxMeanVecOriginal = np.nanmax(meanVecOriginal)
930 deltaXlim = maxMeanVecOriginal - minMeanVecOriginal
931 a.plot(meanVecFit, ptcFunc(pars, meanVecFit), color=
'red')
932 a.plot(meanVecFinal, ptcNoiseAdu**2 + (1./ptcGain)*meanVecFinal, color=
'green',
934 a.scatter(meanVecFinal, varVecFinal, c=
'blue', marker=
'o', s=markerSize)
935 a.scatter(meanVecOutliers, varVecOutliers, c=
'magenta', marker=
's', s=markerSize)
936 a.text(0.03, 0.66, stringLegend, transform=a.transAxes, fontsize=legendFontSize)
937 a.set_title(amp, fontsize=titleFontSize)
938 a.set_xlim([minMeanVecOriginal - 0.2*deltaXlim, maxMeanVecOriginal + 0.2*deltaXlim])
941 a2.plot(meanVecFit, ptcFunc(pars, meanVecFit), color=
'red')
942 a2.scatter(meanVecFinal, varVecFinal, c=
'blue', marker=
'o', s=markerSize)
943 a2.scatter(meanVecOutliers, varVecOutliers, c=
'magenta', marker=
's', s=markerSize)
944 a2.text(0.03, 0.66, stringLegend, transform=a2.transAxes, fontsize=legendFontSize)
945 a2.set_title(amp, fontsize=titleFontSize)
946 a2.set_xlim([minMeanVecOriginal, maxMeanVecOriginal])
949 a3.plot(meanVecFit, ptcFunc(pars, meanVecFit)/meanVecFit, color=
'red')
950 a3.scatter(meanVecFinal, varVecFinal/meanVecFinal, c=
'blue', marker=
'o', s=markerSize)
951 a3.scatter(meanVecOutliers, varVecOutliers/meanVecOutliers, c=
'magenta', marker=
's',
953 a3.text(0.05, 0.1, stringLegend, transform=a3.transAxes, fontsize=legendFontSize)
954 a3.set_title(amp, fontsize=titleFontSize)
955 a3.set_xlim([minMeanVecOriginal - 0.2*deltaXlim, maxMeanVecOriginal + 0.2*deltaXlim])
957 f.suptitle(
"PTC \n Fit: " + stringTitle, fontsize=supTitleFontSize)
959 f2.suptitle(
"PTC (log-log)", fontsize=supTitleFontSize)
961 f3.suptitle(
r"Var/$\mu$", fontsize=supTitleFontSize)
966 def _plotLinearizer(self, dataset, linearizer, pdfPages):
967 """Plot linearity and linearity residual per amplifier
971 dataset : `lsst.ip.isr.ptcDataset.PhotonTransferCurveDataset`
972 The dataset containing the means, variances, exposure times, and mask.
974 linearizer : `lsst.ip.isr.Linearizer`
980 supTitleFontSize = 18
983 nAmps = len(dataset.ampNames)
986 nRows = np.sqrt(nAmps)
987 mantissa, _ = np.modf(nRows)
989 nRows = int(nRows) + 1
996 f, ax = plt.subplots(nrows=nRows, ncols=nCols, sharex=
'col', sharey=
'row', figsize=(13, 10))
997 f2, ax2 = plt.subplots(nrows=nRows, ncols=nCols, sharex=
'col', sharey=
'row', figsize=(13, 10))
998 for i, (amp, a, a2)
in enumerate(zip(dataset.ampNames, ax.flatten(), ax2.flatten())):
999 mask = dataset.expIdMask[amp]
1000 if np.isnan(mask[0]):
1001 a.set_title(f
"{amp} (BAD)", fontsize=titleFontSize)
1002 a2.set_title(f
"{amp} (BAD)", fontsize=titleFontSize)
1005 mask = mask.astype(bool)
1006 meanVecFinal = np.array(dataset.rawMeans[amp])[mask]
1007 timeVecFinal = np.array(dataset.rawExpTimes[amp])[mask]
1009 a.set_xlabel(
'Time (sec)', fontsize=labelFontSize)
1010 a.set_ylabel(
r'Mean signal ($\mu$, ADU)', fontsize=labelFontSize)
1011 a.tick_params(labelsize=labelFontSize)
1012 a.set_xscale(
'linear')
1013 a.set_yscale(
'linear')
1015 a2.axhline(y=0, color=
'k')
1016 a2.axvline(x=0, color=
'k', linestyle=
'-')
1017 a2.set_xlabel(
r'Mean signal ($\mu$, ADU)', fontsize=labelFontSize)
1018 a2.set_ylabel(
'Fractional nonlinearity (%)', fontsize=labelFontSize)
1019 a2.tick_params(labelsize=labelFontSize)
1020 a2.set_xscale(
'linear')
1021 a2.set_yscale(
'linear')
1023 pars, parsErr = linearizer.fitParams[amp], linearizer.fitParamsErr[amp]
1024 k0, k0Error = pars[0], parsErr[0]
1025 k1, k1Error = pars[1], parsErr[1]
1026 k2, k2Error = pars[2], parsErr[2]
1027 linRedChi2 = linearizer.fitChiSq[amp]
1028 stringLegend = (f
"k0: {k0:.4}+/-{k0Error:.2e} ADU\nk1: {k1:.4}+/-{k1Error:.2e} ADU/t"
1029 f
"\nk2: {k2:.2e}+/-{k2Error:.2e} ADU/t^2\n"
1030 r"$\chi^2_{\rm{red}}$: " + f
"{linRedChi2:.4}")
1031 a.scatter(timeVecFinal, meanVecFinal)
1032 a.plot(timeVecFinal,
funcPolynomial(pars, timeVecFinal), color=
'red')
1033 a.text(0.03, 0.75, stringLegend, transform=a.transAxes, fontsize=legendFontSize)
1034 a.set_title(f
"{amp}", fontsize=titleFontSize)
1036 linearPart = k0 + k1*timeVecFinal
1037 fracLinRes = 100*(linearPart - meanVecFinal)/linearPart
1038 a2.plot(meanVecFinal, fracLinRes, c=
'g')
1039 a2.set_title(f
"{amp}", fontsize=titleFontSize)
1041 f.suptitle(
"Linearity \n Fit: Polynomial (degree: %g)"
1043 fontsize=supTitleFontSize)
1044 f2.suptitle(
r"Fractional NL residual" "\n"
1045 r"$100\times \frac{(k_0 + k_1*Time-\mu)}{k_0+k_1*Time}$",
1046 fontsize=supTitleFontSize)
1048 pdfPages.savefig(f2)
1052 """Group data into bins, with at most maxDiff distance between bins.
1060 Maximum distance between bins.
1069 index = np.zeros_like(x, dtype=np.int32)
1074 for i
in range(1, len(ix)):
1076 if (xval - xc < maxDiff):
1077 xc = (ng*xc + xval)/(ng+1)
1079 index[ix[i]] = group
1083 index[ix[i]] = group
1090 """Builds an index with regular binning. The result can be fed into binData.
1101 np.digitize(x, bins): `numpy.array`
1105 bins = np.linspace(x.min(), x.max() +
abs(x.max() * 1e-7), nBins + 1)
1106 return np.digitize(x, bins)
1110 """Bin data (usually for display purposes).
1121 Bin number of each datum.
1124 Inverse rms of each datum to use when averaging (the actual weight is wy**2).
1135 wybin: `numpy.array`
1136 Binned weights in y, computed from wy's in each bin.
1138 sybin: `numpy.array`
1139 Uncertainty on the bin average, considering actual scatter, and ignoring weights.
1143 wy = np.ones_like(x)
1144 binIndexSet =
set(binIndex)
1147 xbin = np.array([xw2[binIndex == i].sum()/w2[binIndex == i].sum()
for i
in binIndexSet])
1150 ybin = np.array([yw2[binIndex == i].sum()/w2[binIndex == i].sum()
for i
in binIndexSet])
1152 wybin = np.sqrt(np.array([w2[binIndex == i].sum()
for i
in binIndexSet]))
1153 sybin = np.array([y[binIndex == i].
std()/np.sqrt(np.array([binIndex == i]).sum())
1154 for i
in binIndexSet])
1156 return xbin, ybin, wybin, sybin
def _plotStandardPtc(self, dataset, ptcFitType, pdfPages)
def plotRelativeBiasACoeffs(aDict, aDictNoB, fullCovsModel, fullCovsModelNoB, signalElectrons, gainDict, pdfPages, maxr=None)
plotNormalizedCovariancesNumberOfBins
def __init__(self, datasetFilename, linearizerFileName=None, outDir='.', detNum=999, signalElectronsRelativeA=75000, plotNormalizedCovariancesNumberOfBins=10)
def indexForBins(x, nBins)
def plotCovariances(mu, covs, covsModel, covsWeights, covsNoB, covsModelNoB, covsWeightsNoB, gainDict, noiseDict, aDict, bDict, pdfPages)
def ab_vs_dist(aDict, bDict, pdfPages, bRange=4)
def plotAcoeffsSum(aDict, bDict, pdfPages)
def plotNormalizedCovariances(self, i, j, inputMu, covs, covsModel, covsWeights, covsNoB, covsModelNoB, covsWeightsNoB, pdfPages, offset=0.004, numberOfBins=10, plotData=True, topPlot=False, log=None)
def binData(x, y, binIndex, wy=None)
def _plotLinearizer(self, dataset, linearizer, pdfPages)
def findGroups(x, maxDiff)
def run(self, filenameFull, datasetPtc, linearizer=None, log=None)
def plot_a_b(aDict, bDict, pdfPages, bRange=3)
def covAstierMakeAllPlots(self, dataset, pdfPages, log=None)
daf::base::PropertyList * list
daf::base::PropertySet * set
def getFitDataFromCovariances(i, j, mu, fullCov, fullCovModel, fullCovSqrtWeights, gain=1.0, divideByMu=False, returnMasked=False)
def calculateWeightedReducedChi2(measured, model, weightsMeasured, nData, nParsModel)
def funcPolynomial(pars, x)
bool all(CoordinateExpr< N > const &expr) noexcept
Return true if all elements are true.
Angle abs(Angle const &a)