Skip to content

Commit

Permalink
Fixes to allow cameraGeom Builder() methods to function correctly.
Browse files Browse the repository at this point in the history
* Add AssemblyState enum for amplifiers to increase flexibility of hasRawInfo().
* Add getDataBBox() accessor to amplifiers to provide consistent BBox access,
  regardless of AssemblyState.
* Add helpers to CameraConfig class to generate correctly typed values from
  input.
* Simplify CameraFactory to use fromConfig() and new CameraConfig methods to
  construct detectors.
* Add method to show transformMap connections (as there was previously no way to
  inspect this easily from python).
  • Loading branch information
czwa committed Sep 30, 2019
1 parent a2584b0 commit d8bbf7c
Show file tree
Hide file tree
Showing 15 changed files with 113 additions and 38 deletions.
8 changes: 8 additions & 0 deletions include/lsst/afw/cameraGeom/Amplifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ enum class ReadoutCorner {
UL,
};

/**
* Assembly state of the amplifier, used to identify bounding boxes and component existence.
*/
enum class AssemblyState {
RAW,
SCIENCE,
};

/**
* Geometry and electronic information about raw amplifier images
*
Expand Down
2 changes: 1 addition & 1 deletion include/lsst/afw/cameraGeom/Detector.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ class DetectorBase {
class Detector final :
public DetectorBase,
public table::io::PersistableFacade<Detector>,
public table::io::Persistable
public typehandling::Storable
{
public:

Expand Down
3 changes: 3 additions & 0 deletions python/lsst/afw/cameraGeom/amplifier/amplifier.cc
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ PyAmplifier declarePyAmplifier(py::module & mod) {
.value("LR", ReadoutCorner::LR)
.value("UR", ReadoutCorner::UR)
.value("UL", ReadoutCorner::UL);
py::enum_<AssemblyState>(mod, "AssemblyState")
.value("RAW", AssemblyState::RAW)
.value("SCIENCE", AssemblyState::SCIENCE);
PyAmplifier cls(mod, "Amplifier");
cls.def_static("getRecordSchema", &Amplifier::getRecordSchema);
cls.def("toRecord", &Amplifier::toRecord);
Expand Down
13 changes: 12 additions & 1 deletion python/lsst/afw/cameraGeom/amplifier/amplifierContinued.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@

__all__ = ["ReadoutCornerValNameDict", "ReadoutCornerNameValDict"]

from lsst.utils import continueClass
from .amplifier import Amplifier, ReadoutCorner, AssemblyState

from .amplifier import ReadoutCorner

ReadoutCornerValNameDict = {
ReadoutCorner.LL: "LL",
Expand All @@ -33,3 +34,13 @@
}
ReadoutCornerNameValDict = {val: key for key, val in
ReadoutCornerValNameDict.items()}


@continueClass # noqa: F811
class Amplifier:
def getDataBBox(self):
myState = self.getAssemblyState()
if myState == AssemblyState.SCIENCE:
return self.getBBox()
else:
return self.getRawDataBBox()
40 changes: 40 additions & 0 deletions python/lsst/afw/cameraGeom/cameraConfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

import numpy as np
import lsst.pex.config as pexConfig
import lsst.geom as geom
from .orientation import Orientation
from .transformConfig import TransformMapConfig


Expand Down Expand Up @@ -75,15 +77,53 @@ class DetectorConfig(pexConfig.Config):
optional=True
)

# Accessors to get "compiled" versions of parameters.
def getCrosstalk(self, numAmps):
"""Return a 2-D numpy array of crosstalk coefficients of the proper shape"""
if not self.crosstalk:
return None

# CZW: Should this be here, or is obs_lsst just not setting numAmps correctly?
if numAmps != int(np.sqrt(len(self.crosstalk))):
numAmps = int(np.sqrt(len(self.crosstalk)))
try:
return np.array(self.crosstalk, dtype=np.float32).reshape((numAmps, numAmps))
except Exception as e:
raise RuntimeError("Cannot reshape 'crosstalk' coefficients to square matrix: %s" % (e,))

def getBBox(self):
"""Return the detector bounding box from the separate box endpoint
values.
"""
return geom.BoxI(geom.PointI(self.bbox_x0, self.bbox_y0),
geom.PointI(self.bbox_x1, self.bbox_y1))

def getOffset(self):
"""Return the detector offset as a Point2D from the separate config
values.
"""
return geom.Point2D(self.offset_x, self.offset_y)

def getRefPos(self):
"""Return the detector reference position as a Point2D from the
separate config values.
"""
return geom.Point2D(self.refpos_x, self.refpos_y)

def getOrientation(self):
"""Return the cameraGeom.Orientation() object defined by the
configuration values.
"""
return Orientation(self.getOffset(), self.getRefPos(),
geom.Angle(self.yawDeg, geom.degrees),
geom.Angle(self.pitchDeg, geom.degrees),
geom.Angle(self.rollDeg, geom.degrees))

def getPixelSize(self):
"""Return the pixel size as an Extent2D from the separate values.
"""
return geom.Extent2D(self.pixelSize_x, self.pixelSize_y)


class CameraConfig(pexConfig.Config):
"""A configuration that represents (and can be used to construct) a Camera.
Expand Down
38 changes: 15 additions & 23 deletions python/lsst/afw/cameraGeom/cameraFactory.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

__all__ = ["makeCameraFromPath", "makeCameraFromAmpLists"]
__all__ = ["addDetectorBuilderFromConfig", "addDetectorFromConfig",
"makeCameraFromPath", "makeCameraFromAmpLists"]

import os.path
import lsst.geom
from lsst.afw.table import BaseCatalog
from .cameraGeomLib import FOCAL_PLANE, FIELD_ANGLE, PIXELS, TAN_PIXELS, ACTUAL_PIXELS, CameraSys, \
DetectorType, Orientation, Amplifier
Amplifier
from .camera import Camera
from .makePixelToTanPixel import makePixelToTanPixel
from .pupil import PupilFactory
Expand Down Expand Up @@ -64,17 +64,7 @@ def addDetectorBuilderFromConfig(cameraBuilder, detectorConfig, amplifiers, foca
associated with the given camera builder object.
"""
detectorBuilder = cameraBuilder.add(detectorConfig.name, detectorConfig.id)
detectorBuilder.setType(DetectorType(detectorConfig.detectorType))
detectorBuilder.setSerial(detectorConfig.serial)
detectorBuilder.setPhysicalType(detectorConfig.physicalType)
detectorBuilder.setOrientation(makeOrientation(detectorConfig))
detectorBuilder.setPixelSize(lsst.geom.Extent2D(detectorConfig.pixelSize_x, detectorConfig.pixelSize_y))
detectorBuilder.setBBox(
lsst.geom.Box2I(
minimum=lsst.geom.Point2I(detectorConfig.bbox_x0, detectorConfig.bbox_y0),
maximum=lsst.geom.Point2I(detectorConfig.bbox_x1, detectorConfig.bbox_y1),
)
)
detectorBuilder.fromConfig(detectorConfig)

for ampBuilder in amplifiers:
detectorBuilder.append(ampBuilder)
Expand All @@ -96,19 +86,26 @@ def addDetectorBuilderFromConfig(cameraBuilder, detectorConfig, amplifiers, foca
# lots of on-disk camera configs.
assert detectorNativeSysPrefix == PIXELS, "Detectors with nativeSys != PIXELS are not supported."

for toSys, transform in transforms.items():
detectorBuilder.setTransformFromPixelsTo(toSys, transform)
tanPixSys = CameraSys(TAN_PIXELS, detectorConfig.name)
transforms[tanPixSys] = makePixelToTanPixel(
bbox=detectorBuilder.getBBox(),
orientation=detectorBuilder.getOrientation(),
focalPlaneToField=focalPlaneToField,
pixelSizeMm=detectorBuilder.getPixelSize(),
)

for toSys, transform in transforms.items():
detectorBuilder.setTransformFromPixelsTo(toSys, transform)

detectorBuilder.setCrosstalk(detectorConfig.getCrosstalk(len(amplifiers)))

return detectorBuilder


def addDetectorFromConfig(cameraBuilder, detectorConfig, amplifiers, focalPlaneToField):
return addDetectorBuilderFromConfig(cameraBuilder, detectorConfig, amplifiers, focalPlaneToField)


def makeOrientation(detectorConfig):
"""Make an Orientation instance from a detector config
Expand All @@ -122,12 +119,7 @@ def makeOrientation(detectorConfig):
orientation : `lsst.afw.cameraGeom.Orientation`
Location and rotation of the Detector.
"""
offset = lsst.geom.Point2D(detectorConfig.offset_x, detectorConfig.offset_y)
refPos = lsst.geom.Point2D(detectorConfig.refpos_x, detectorConfig.refpos_y)
yaw = lsst.geom.Angle(detectorConfig.yawDeg, lsst.geom.degrees)
pitch = lsst.geom.Angle(detectorConfig.pitchDeg, lsst.geom.degrees)
roll = lsst.geom.Angle(detectorConfig.rollDeg, lsst.geom.degrees)
return Orientation(offset, refPos, yaw, pitch, roll)
return detectorConfig.getOrientation()


def makeTransformDict(transformConfigDict):
Expand Down Expand Up @@ -179,7 +171,7 @@ def makeCameraFromPath(cameraConfig, ampInfoPath, shortNameFunc,
shortName = shortNameFunc(detectorConfig.name)
ampCatPath = os.path.join(ampInfoPath, shortName + ".fits")
catalog = BaseCatalog.readFits(ampCatPath)
ampListDict[detectorConfig.name] = [Amplifier.Builder.fromRecord(record).finish()
ampListDict[detectorConfig.name] = [Amplifier.Builder.fromRecord(record)
for record in catalog]

return makeCameraFromAmpLists(cameraConfig, ampListDict, pupilFactoryClass)
Expand Down
3 changes: 2 additions & 1 deletion python/lsst/afw/cameraGeom/detector/detector.cc
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ namespace cameraGeom {
namespace {

using PyDetectorBase = py::class_<DetectorBase, std::shared_ptr<DetectorBase>>;
using PyDetector = py::class_<Detector, DetectorBase, std::shared_ptr<Detector>>;
using PyDetector = py::class_<Detector, DetectorBase, std::shared_ptr<Detector>, typehandling::Storable>;
using PyDetectorBuilder = py::class_<Detector::Builder, DetectorBase, std::shared_ptr<Detector::Builder>>;
using PyDetectorPartialRebuilder = py::class_<Detector::PartialRebuilder, Detector::Builder,
std::shared_ptr<Detector::PartialRebuilder>>;
Expand Down Expand Up @@ -111,6 +111,7 @@ void declareDetectorPartialRebuilder(PyDetector & parent);
void declareDetectorInCameraBuilder(PyDetector & parent);

void declareDetector(py::module & mod) {
py::module::import("lsst.afw.typehandling");
PyDetector cls(mod, "Detector");
declareDetectorBuilder(cls);
declareDetectorPartialRebuilder(cls);
Expand Down
26 changes: 23 additions & 3 deletions python/lsst/afw/cameraGeom/detector/detectorContinued.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,33 @@
# see <http://www.lsstcorp.org/LegalNotices/>.
#

__all__ = ["Detector"]
__all__ = ["DetectorBase", "DetectorTypeValNameDict", "DetectorTypeNameValDict"]

from lsst.utils import continueClass
from .detector import Detector
from .detector import DetectorBase, DetectorType

DetectorTypeValNameDict = {
DetectorType.SCIENCE: "SCIENCE",
DetectorType.FOCUS: "FOCUS",
DetectorType.GUIDER: "GUIDER",
DetectorType.WAVEFRONT: "WAVEFRONT",
}

DetectorTypeNameValDict = {val: key for key, val in
DetectorTypeValNameDict.items()}


@continueClass # noqa: F811
class Detector:
class DetectorBase:
def __iter__(self):
return (self[i] for i in range(len(self)))

def fromConfig(self, config=None, numAmps=1):
if config is not None:
self.setSerial(config.serial)
self.setType(DetectorType(config.detectorType))
self.setPhysicalType(config.physicalType)
self.setBBox(config.getBBox())
self.setPixelSize(config.getPixelSize())
self.setOrientation(config.getOrientation())
self.setCrosstalk(config.getCrosstalk(numAmps))
1 change: 1 addition & 0 deletions python/lsst/afw/cameraGeom/transformMap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ void declareTransformMap(py::module & mod) {
"pointList"_a, "fromSys"_a, "toSys"_a
);
cls.def("getTransform", &TransformMap::getTransform, "fromSys"_a, "toSys"_a);
cls.def("listConnections", &TransformMap::getConnections);

table::io::python::addPersistableMethods(cls);

Expand Down
9 changes: 5 additions & 4 deletions python/lsst/afw/cameraGeom/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import lsst.afw.geom as afwGeom
import lsst.afw.image as afwImage
import lsst.afw.math as afwMath
import lsst.afw.cameraGeom as afwCameraGeom
import lsst.daf.base as dafBase
import lsst.log
import lsst.pex.exceptions as pexExceptions
Expand Down Expand Up @@ -219,18 +220,18 @@ def makeImageFromAmp(amp, imValue=None, imageFactory=afwImage.ImageU, markSize=1
img.set(imValue)
# Set the first pixel read to a different value
markbbox = lsst.geom.Box2I()
if amp.getReadoutCorner() == 0:
if amp.getReadoutCorner() == afwCameraGeom.ReadoutCorner.LL:
markbbox.include(dbbox.getMin())
markbbox.include(dbbox.getMin() + lsst.geom.Extent2I(markSize, markSize))
elif amp.getReadoutCorner() == 1:
elif amp.getReadoutCorner() == afwCameraGeom.ReadoutCorner.LR:
cornerPoint = lsst.geom.Point2I(dbbox.getMaxX(), dbbox.getMinY())
markbbox.include(cornerPoint)
markbbox.include(cornerPoint + lsst.geom.Extent2I(-markSize, markSize))
elif amp.getReadoutCorner() == 2:
elif amp.getReadoutCorner() == afwCameraGeom.ReadoutCorner.UR:
cornerPoint = lsst.geom.Point2I(dbbox.getMax())
markbbox.include(cornerPoint)
markbbox.include(cornerPoint + lsst.geom.Extent2I(-markSize, -markSize))
elif amp.getReadoutCorner() == 3:
elif amp.getReadoutCorner() == afwCameraGeom.ReadoutCorner.UL:
cornerPoint = lsst.geom.Point2I(dbbox.getMinX(), dbbox.getMaxY())
markbbox.include(cornerPoint)
markbbox.include(cornerPoint + lsst.geom.Extent2I(markSize, -markSize))
Expand Down
1 change: 0 additions & 1 deletion python/lsst/afw/table/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
from ._schema import *
from ._baseColumnView import *
from ._base import *
from ._idFactory import *
from ._aggregates import *
from ._arrays import *
from ._simple import *
Expand Down
2 changes: 0 additions & 2 deletions python/lsst/afw/table/_table.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ using utils::python::WrapperCollection;

void wrapAggregates(WrapperCollection&);
void wrapAliasMap(WrapperCollection&);
void wrapAmpInfo(WrapperCollection&);
void wrapArrays(WrapperCollection&);
void wrapBase(WrapperCollection&);
void wrapBaseColumnView(WrapperCollection&);
Expand All @@ -63,7 +62,6 @@ PYBIND11_MODULE(_table, mod) {
wrapSlots(wrappers);
wrapSimple(wrappers);
wrapSource(wrappers);
wrapAmpInfo(wrappers);
wrapExposure(wrappers);
wrapMatch(wrappers);
wrapWcsUtils(wrappers);
Expand Down
2 changes: 1 addition & 1 deletion src/cameraGeom/Amplifier.cc
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ struct RecordSchemaHelper {
linearityMaximum(schema.addField<double>("linearityMaximum", "TODO! NEVER DOCUMENTED")),
linearityUnits(schema.addField<std::string>("linearityUnits", "TODO! NEVER DOCUMENTED", "", 0))
{
schema.getCitizen().markPersistent();

}

};
Expand Down
2 changes: 1 addition & 1 deletion src/cameraGeom/TransformMap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ struct PersistenceHelper {
"Camera coordinate system detector name.", "", 0)),
transform(schema.addField<int>("transform", "Archive ID of the transform.", ""))
{
schema.getCitizen().markPersistent();

}

PersistenceHelper(PersistenceHelper const &) = delete;
Expand Down
1 change: 1 addition & 0 deletions src/table/io/FitsReader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Registry& getRegistry() {
}

static FitsReader const baseFitsReader("BASE");
static FitsReader const ampInfoFitsReader("AMPINFO");

} // namespace

Expand Down

0 comments on commit d8bbf7c

Please sign in to comment.