Skip to content

Commit da8b983

Browse files
Merge pull request #67 from lsst-sitcom/tickets/DM-53231
DM-53231: Make PsfShapeAzEl have higher spatial resolution
2 parents 7108416 + a61a6f4 commit da8b983

File tree

1 file changed

+74
-32
lines changed

1 file changed

+74
-32
lines changed

python/lsst/summit/extras/plotting/psfPlotting.py

Lines changed: 74 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,8 @@ def makeFigureAndAxes(nrows=2) -> tuple[Figure, Any]:
421421
def plotData(
422422
axs: npt.NDArray[np.object_],
423423
table: Table,
424+
maxPointsPerDetector: int = 60,
425+
minPointsPerDetector: int = 5,
424426
prefix: str = "",
425427
) -> None:
426428
"""Plot the data from the table on the provided figure and axes.
@@ -431,38 +433,61 @@ def plotData(
431433
The array of axes objects to plot on.
432434
table : `astropy.table.Table`
433435
The table containing the data to be plotted.
436+
maxPointsPerDetector : `int`, optional
437+
The maximum number of points per detector to plot. If the number of
438+
points in the table is greater than this value, a random subset of
439+
points will be plotted.
440+
minPointsPerDetector : `int`, optional
441+
The minimum number of points per detector to plot. If the number of
442+
points in the table is less than this value,
443+
all points will be plotted.
434444
prefix : `str`, optional
435445
The prefix to be added to the column names of the rotated shapes.
436446
"""
447+
tableDownsampled = randomRowsPerDetector(table, minPointsPerDetector)
448+
table = randomRowsPerDetector(table, maxPointsPerDetector)
449+
437450
x = table[prefix + "x"]
438451
y = table[prefix + "y"]
439452
e1 = table[prefix + "e1"]
440453
e2 = table[prefix + "e2"]
441454
e = table["e"]
442455
fwhm = table["FWHM"]
443456

457+
xDownsampled = tableDownsampled[prefix + "x"]
458+
yDownsampled = tableDownsampled[prefix + "y"]
459+
e1Downsampled = tableDownsampled[prefix + "e1"]
460+
e2Downsampled = tableDownsampled[prefix + "e2"]
461+
eDownsampled = tableDownsampled["e"]
462+
444463
# Quiver plot
445-
quiver_kwargs = {
464+
quiverKwargs = {
446465
"headlength": 0,
447466
"headaxislength": 0,
448467
"scale": QUIVER_SCALE,
449468
"pivot": "middle",
450469
}
451470

452-
shape_angle = 0.5 * np.arctan2(e2, e1) # spin-2
453-
Q_shape = axs[0, 0].quiver(x, y, e * np.cos(shape_angle), e * np.sin(shape_angle), **quiver_kwargs)
454-
axs[0, 1].quiverkey(Q_shape, X=0.08, Y=0.95, U=0.2, label="0.2", labelpos="S")
471+
shapeAngle = 0.5 * np.arctan2(e2Downsampled, e1Downsampled) # spin-2
472+
qShape = axs[0, 0].quiver(
473+
xDownsampled,
474+
yDownsampled,
475+
eDownsampled * np.cos(shapeAngle),
476+
eDownsampled * np.sin(shapeAngle),
477+
**quiverKwargs,
478+
)
479+
axs[0, 1].quiverkey(qShape, X=0.08, Y=0.95, U=0.2, label="0.2", labelpos="S")
455480

456481
# FWHM plot
457-
cbar = addColorbarToAxes(axs[0, 1].scatter(x, y, c=fwhm, s=5))
482+
cbar = addColorbarToAxes(axs[0, 1].scatter(x, y, c=fwhm, s=1))
458483
cbar.set_label("FWHM [arcsec]")
459484

460485
# Ellipticity plots
461486
emax = np.quantile(np.abs(np.concatenate([e1, e2])), 0.98)
462-
axs[1, 0].scatter(x, y, c=e1, vmin=-emax, vmax=emax, cmap="bwr", s=5)
487+
axs[1, 0].scatter(x, y, c=e1, vmin=-emax, vmax=emax, cmap="bwr", s=1)
463488
axs[1, 0].text(0.05, 0.92, "e1", transform=axs[1, 0].transAxes, fontsize=10)
464489

465-
cbar = addColorbarToAxes(axs[1, 1].scatter(x, y, c=e2, vmin=-emax, vmax=emax, cmap="bwr", s=5))
490+
cbar = addColorbarToAxes(axs[1, 1].scatter(x, y, c=e2, vmin=-emax, vmax=emax, cmap="bwr", s=1))
466491
cbar.set_label("e")
467492
axs[1, 1].text(0.89, 0.92, "e2", transform=axs[1, 1].transAxes, fontsize=10)
468493

@@ -511,6 +536,7 @@ def plotHigherOrderMomentsData(
511536
table: Table,
512537
prefix: str = "",
513538
binSpacing: float = 0.1,
539+
maxPointsPerDetector: int = 5,
514540
):
515541
"""Plot coma, trefoil and kurtosis from the table on the provided axes.
516542
@@ -524,12 +550,16 @@ def plotHigherOrderMomentsData(
524550
The prefix to use for the column names in the table.
525551
binSpacing : `float`, optional
526552
The spacing between arrows and triangles in the plot.
553+
maxPointsPerDetector : `int`, optional
554+
The maximum number of points per detector to plot. If the number of
555+
points exceeds this value, a random subset will be selected.
527556
"""
557+
table = randomRowsPerDetector(table, maxPointsPerDetector)
528558
x = table[prefix + "x"]
529559
y = table[prefix + "y"]
530560
kurtosis = table["kurtosis"]
531561

532-
quiver_kwargs = {
562+
quiverKwargs = {
533563
"headlength": 5,
534564
"headaxislength": 5,
535565
"scale": 1,
@@ -538,42 +568,49 @@ def plotHigherOrderMomentsData(
538568
}
539569

540570
coords = np.vstack([x, y]).T
541-
mean_coma = {}
571+
meanComa = {}
542572
for i in (1, 2):
543573
binning = meanify(binSpacing)
544574
binning.add_field(coords, table[f"coma{i}"])
545575
binning.meanify()
546-
mean_coma[i] = binning.params0
576+
meanComa[i] = binning.params0
547577

548-
mean_coma_angle = np.arctan2(mean_coma[2], mean_coma[1])
549-
mean_coma_amplitude = np.hypot(mean_coma[2], mean_coma[2])
550-
Q_coma = axs[0].quiver(
578+
meanComaAngle = np.arctan2(meanComa[2], meanComa[1])
579+
meanComaAmplitude = np.hypot(meanComa[2], meanComa[2])
580+
qComa = axs[0].quiver(
551581
binning.coords0[:, 0],
552582
binning.coords0[:, 1],
553-
mean_coma_amplitude * np.cos(mean_coma_angle),
554-
mean_coma_amplitude * np.sin(mean_coma_angle),
555-
**quiver_kwargs,
583+
meanComaAmplitude * np.cos(meanComaAngle),
584+
meanComaAmplitude * np.sin(meanComaAngle),
585+
**quiverKwargs,
556586
)
557-
axs[0].quiverkey(Q_coma, X=0.1, Y=0.88, U=0.05, label="0.05", labelpos="S")
587+
axs[0].quiverkey(qComa, X=0.1, Y=0.88, U=0.05, label="0.05", labelpos="S")
558588

559-
mean_trefoil = {}
589+
meanTrefoil = {}
560590
for i in (1, 2):
561591
binning = meanify(binSpacing)
562592
binning.add_field(coords, table[f"trefoil{i}"])
563593
binning.meanify()
564-
mean_trefoil[i] = binning.params0
594+
meanTrefoil[i] = binning.params0
565595

566-
mean_trefoil_angle = np.arctan2(mean_trefoil[2], mean_trefoil[1]) / 3 # spin-3
567-
mean_trefoil_amplitude = np.hypot(mean_trefoil[2], mean_trefoil[1])
568-
SCALE_TRIANGLE = 500
569-
axs[1].scatter(1.8, 1.7, s=0.1 * SCALE_TRIANGLE, marker=(3, 0, 30), lw=0.1, color="black")
596+
meanTrefoilAngle = np.arctan2(meanTrefoil[2], meanTrefoil[1]) / 3 # spin-3
597+
meanTrefoilAmplitude = np.hypot(meanTrefoil[2], meanTrefoil[1])
598+
scaleTriangle = 500
599+
axs[1].scatter(1.8, 1.7, s=0.1 * scaleTriangle, marker=(3, 0, 30), lw=0.1, color="black")
570600
axs[1].text(1.6, 1.35, "0.1")
571-
for idx in range(len(mean_trefoil_amplitude)):
572-
_t = mean_trefoil_amplitude[idx]
573-
_ta = mean_trefoil_angle[idx] * 180 / np.pi
574-
_xcen = binning.coords0[idx, 0]
575-
_ycen = binning.coords0[idx, 1]
576-
axs[1].scatter(_xcen, _ycen, marker=(3, 0, 30 + _ta), s=_t * SCALE_TRIANGLE, lw=0.1, color="black")
601+
for idx in range(len(meanTrefoilAmplitude)):
602+
tVal = meanTrefoilAmplitude[idx]
603+
trefoilAngleDeg = meanTrefoilAngle[idx] * 180 / np.pi
604+
xCenter = binning.coords0[idx, 0]
605+
yCenter = binning.coords0[idx, 1]
606+
axs[1].scatter(
607+
xCenter,
608+
yCenter,
609+
marker=(3, 0, 30 + trefoilAngleDeg),
610+
s=tVal * scaleTriangle,
611+
lw=0.1,
612+
color="black",
613+
)
577614

578615
pos = axs[1].get_position() # get current position [left, bottom, width, height]
579616
axs[1].set_position([pos.x0, pos.y0, pos.width * 0.931, pos.height])
@@ -896,7 +933,8 @@ def makeAzElPlot(
896933
axs: npt.NDArray[np.object_],
897934
table: Table,
898935
camera: Camera,
899-
maxPointsPerDetector: int = 5,
936+
maxPointsPerDetector: int = 60,
937+
minPointsPerDetector: int = 5,
900938
saveAs: str = "",
901939
) -> None:
902940
"""Plot the PSFs on the focal plane, rotated to az/el coordinates.
@@ -931,6 +969,10 @@ def makeAzElPlot(
931969
The maximum number of points per detector to plot. If the number of
932970
points in the table is greater than this value, a random subset of
933971
points will be plotted.
972+
minPointsPerDetector : `int`, optional
973+
The minimum number of points per detector to plot. If the number of
974+
points in the table is less than this value,
975+
all points will be plotted.
934976
saveAs : `str`, optional
935977
The file path to save the figure.
936978
"""
@@ -939,8 +981,8 @@ def makeAzElPlot(
939981

940982
if "kurtosis" in table.columns and axs.shape[0] > 2:
941983
plotHigherOrderMomentsData(axs[2, :], table, prefix="aa_")
942-
table = randomRowsPerDetector(table, maxPointsPerDetector)
943-
plotData(axs[:2, :], table, prefix="aa_")
984+
985+
plotData(axs[:2, :], table, maxPointsPerDetector, minPointsPerDetector, prefix="aa_")
944986

945987
oneRaftOnly = camera.getName() in ["LSSTComCam", "LSSTComCamSim", "TS8"]
946988
plotLimit = 90 * MM_TO_DEG if oneRaftOnly else 90 * MM_TO_DEG * FULL_CAMERA_FACTOR

0 commit comments

Comments
 (0)