Skip to content

Instantly share code, notes, and snippets.

@jdswinbank
Last active October 1, 2018 22:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jdswinbank/58579f2db22c14e4909131f35412d13a to your computer and use it in GitHub Desktop.
Save jdswinbank/58579f2db22c14e4909131f35412d13a to your computer and use it in GitHub Desktop.

Quick guide to measurement algorithms and flags

The structure of meas_base measurement

Measurements take place within the meas_base framework.

meas_base distinguishes between a measurement “algorithm” (e.g. a bit of code which takes some pixels and returns some useful result based upon them) and a measurement “driver”, which takes an image and runs a bunch of algorithms on it.

Typical measurement algorithms include (say) PSF fluxes, the SDSS shape algorithm, etc.

The measurement driver depends on the type of measurement being performed. meas_base provides two: single frame measurement, which is measurement on a single visit, and forced measurement, which takes an image and an external catalog which it uses to fix source centroids and shapes.

Measurement drivers are implemented as tasks, while algorithms are meas_base plugins. They can be combined fairly arbitrarily. Thus, the PSF flux algorithm can be used with single frame measurement or forced measurement, etc.

meas_base provides those two drivers and a bunch of simple algorithms. Other packages can provide more of either, following the same conventions. Most of our meas_extensions packages provide measurement algorithms for use in the meas_base framework (e.g. simpleShape, shapeHSM, etc).

For AP's purposes, the most notable extension package is ip_diffim. It provides a driver suitable for meas_base-style measurement on difference images1, and a bunch of algorithms (e.g. dipole measurement) which are relevant to us.

Naming and selecting measurement algorithms

For the purposes of the current discussion, measurement drivers are fairly academic: we have the ones we need for now, and they mostly “just work”. The interesting stuff all happens in algorithms.

First, understand that algorithms may be referred to by strings which describe both their home package and their name. Thus, base_PsfFlux refers to the PsfFlux algorithm in meas_base.

When configuring a measurement driver (i.e. a task like SingleFrameMeasurementTask) we specify a list of plugins to be run when executing the driver. See, for example, here. These can be over-ridden by the user in task configuration.

When the driver runs, it will return an afw::table. Each row in the table corresponds to a particular source. Each algorithm plugin can add columns to the tables to contain its results. They follow the same naming scheme. Thus, if the base_PsfFlux algorithm is run, you'll see a bunch of fields in the table with names starting base_PsfFlux_base_PsfFlux_instFlux, base_PsfFlux_instFluxErr, etc.

(Incidentally, note that this means that the schema of the output table depends on the particular plugins that were enabled at task configuration time. That's why it's not easy to provide a single document that describes pipeline outputs — they vary with user configuration.)

Algorithm fields and flags

Following from the above, when a new measurement driver (task) is created, it will:

  1. Create a new schema for its output;
  2. Hand that to each of the measurement algorithms (plugins) it's planning to run, and ask them to add fields which will be used to store their outputs.

In the limit of poor documentation, we can look at the code for the algorithm to understand what fields it's adding and what they mean.

Note that algorithms can be implemented in C++ or Python, so we need to be able to look at both.

Here's an example in Python: the base_FPPosition plugin. When this plugin is created, it is given a schema argument, which will ultimately be the schema of the measurement driver outputs. It adds three things to the schema: fields to contain its outputs, and two flag fields:

schema.addField(name + "_flag", type="Flag", doc="Set to True for any fatal failure")
schema.addField(name + "_missingDetector_flag", type="Flag", doc="Set to True if detector object is missing")

When you look at the output of any measurement driver that has executed base_FPPosition, you should see those fields in the schema. And you can see that the flags are defined and (briefly) documented: if the algorithm fails for any reason, base_FPPosition_flag will be set to True, and if it fails specifically because the detector is missing, base_FPPosition_missingDetector_flag will be set to True.

A bunch of other plugins are defined in Python and follow the same pattern. See, e.g., base_Jacobian (look here), base_PeakCentroid (this one's here), etc.

Of course, plugins which are complex or have to run fast are written in C++. This follows the same principle, except that the code may be a little less familiar looking. Check out the flags defined for aperture or PSF fluxes, for example.

Flags based on masks

Often we don't care about failures with a particular measurement algorithm, which will be flagged as above, but about whether a source happened to fall on some area of the image which we know is suspect (interpolated, at the edge of the CCD, saturated, whatever).

This information is captured at the pixel level by the image mask planes. A special measurement plugin, base_PixelFlags, can be run to transfer information from the image mask to the source flags. For example, when the base_PixelFlags algorithm is run, it will set a base_PixelFlags_flag_interpolated field to True if any of the pixels in the footprint of the source being measured were marked as INTRP on the image.

Propagating flags

Of course, if you're looking at the association database, you're not actually looking at the output of the measurement driver, but at whatever fields have been copied or otherwise manipulated from that driver output by the association code. If you can't see the flags you expect, likely they aren't being propagated properly.


  1. I assume... I didn't actually check this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment