Index: branches/pap/ppSub/src/Makefile.am
===================================================================
--- branches/pap/ppSub/src/Makefile.am	(revision 23711)
+++ branches/pap/ppSub/src/Makefile.am	(revision 23719)
@@ -13,22 +13,25 @@
 ppSub_LDFLAGS  = $(PSLIB_LIBS)   $(PSMODULE_LIBS)   $(PPSTATS_LIBS)   $(PSPHOT_LIBS)   $(PPSUB_LIBS)
 
-ppSub_SOURCES =			 \
-	ppSub.c			 \
-	ppSubArguments.c	 \
-	ppSubVersion.c	         \
-	ppSubBackground.c	 \
-	ppSubCamera.c		 \
-	ppSubData.c		 \
-	ppSubLoop.c		 \
-	ppSubReadout.c		 \
-	ppSubDefineOutput.c      \
-	ppSubExtras.c            \
-	ppSubMakePSF.c           \
-	ppSubMatchPSFs.c         \
-	ppSubReadoutPhotometry.c \
-	ppSubReadoutSubtract.c   \
-	ppSubReadoutUpdate.c     \
-	ppSubSetMasks.c          \
-	ppSubReadoutRenorm.c     \
+ppSub_SOURCES =				\
+	ppSub.c				\
+	ppSubArguments.c		\
+	ppSubVersion.c			\
+	ppSubBackground.c		\
+	ppSubCamera.c			\
+	ppSubData.c			\
+	ppSubErrorCodes.c		\
+	ppSubFiles.c			\
+	ppSubLoop.c			\
+	ppSubDefineOutput.c		\
+	ppSubExtras.c			\
+	ppSubMakePSF.c			\
+	ppSubMatchPSFs.c		\
+	ppSubReadoutInverse.c		\
+	ppSubReadoutJpeg.c		\
+	ppSubReadoutPhotometry.c	\
+	ppSubReadoutStats.c		\
+	ppSubReadoutSubtract.c		\
+	ppSubSetMasks.c			\
+	ppSubReadoutRenorm.c		\
 	ppSubVarianceFactors.c
 
Index: branches/pap/ppSub/src/ppSub.c
===================================================================
--- branches/pap/ppSub/src/ppSub.c	(revision 23711)
+++ branches/pap/ppSub/src/ppSub.c	(revision 23719)
@@ -51,5 +51,5 @@
     ppSubData *data = ppSubDataAlloc(); // Processing data
 
-    if (!ppSubArgumentsSetup(argc, argv, config, data)) {
+    if (!ppSubArguments(argc, argv, config, data)) {
         psErrorStackPrint(stderr, "Error reading arguments.\n");
         exitValue = PS_EXIT_CONFIG_ERROR;
Index: branches/pap/ppSub/src/ppSub.h
===================================================================
--- branches/pap/ppSub/src/ppSub.h	(revision 23711)
+++ branches/pap/ppSub/src/ppSub.h	(revision 23719)
@@ -14,6 +14,9 @@
 #define PP_SUB_H
 
+#include <stdio.h>
 #include <pslib.h>
 #include <psmodules.h>
+
+#include "ppSubErrorCodes.h"
 
 /// @addtogroup ppSub
@@ -28,7 +31,8 @@
     PPSUB_FILES_SUB      = 0x04,        // Subtracted files (output)
     PPSUB_FILES_INV      = 0x08,        // Inverse subtracted files (output)
-    PPSUB_FILES_PHOT     = 0x10,        // psphot photometry files
+    PPSUB_FILES_PSF      = 0x10,        // PSF files (output)
     PPSUB_FILES_PHOT_SUB = 0x20,        // Subtraction photometry files (output)
     PPSUB_FILES_PHOT_INV = 0x40,        // Inverse subtraction photometry files (output)
+    PPSUB_FILES_PHOT     = 0x80,        // General photometry files (internal)
     PPSUB_FILES_ALL      = 0xFF,        // All files
 } ppSubFiles;
@@ -39,5 +43,7 @@
     bool photometry;                    // Perform photometry?
     bool inverse;                       // Output inverse subtraction as well?
-    const char *statsFile;              // Statistics file
+    psString stamps;                    // Stamps file
+    pmPSF *psf;                         // Point Spread Function
+    FILE *statsFile;                    // Statistics file
     psMetadata *stats;                  // Statistics
 } ppSubData;
@@ -47,70 +53,62 @@
 
 /// Setup the arguments parsing
-bool ppSubArgumentsSetup(int argc, char *argv[], ///< Command-line arguments
-                         pmConfig *config    ///< Configuration
-    );
-
-/// Parse the arguments
-bool ppSubArgumentsParse(pmConfig *config ///< Configuration
+bool ppSubArguments(int argc, char *argv[], ///< Command-line arguments
+                    pmConfig *config, ///< Configuration
+                    ppSubData *data ///< Processing data
     );
 
 /// Parse the camera input
-bool ppSubCamera(pmConfig *config       ///< Configuration
+bool ppSubCamera(pmConfig *config,      ///< Configuration
+                 ppSubData *data        ///< Processing data
     );
 
 /// Loop over the FPA hierarchy
-bool ppSubLoop(pmConfig *config         ///< Configuration
+bool ppSubLoop(pmConfig *config,        ///< Configuration
+               ppSubData *data          ///< Processing data
     );
 
 /// Perform PSF-matched image subtraction on the readout
 bool ppSubReadout(pmConfig *config,     ///< Configuration
-                  ppSubData *data,      ///< Processing data
-                  const pmFPAview *view ///< View of readout to subtract
+                  ppSubData *data       ///< Processing data
     );
 
 /// Generate (if needed) and set or update the masks for input and reference images
-bool ppSubSetMasks(pmConfig *config,     ///< Configuration
-                   const pmFPAview *view ///< View of active readout
+bool ppSubSetMasks(pmConfig *config     ///< Configuration
     );
 
 /// Generate the PSF-matching kernel and convolve the images as needed.  Most of this function involves
 /// looking up the parameters in the recipe and supplying them to the function pmSubtractionMatch()
-bool ppSubMatchPSFs(pmConfig *config,    ///< Configuration
-                    ppSubData *data,    ///< Processing data
-                    const pmFPAview *view ///< View of active readout
+bool ppSubMatchPSFs(pmConfig *config,   ///< Configuration
+                    ppSubData *data     ///< Processing data
     );
 
 /// Generate the output readout and pass the kernel info to the header
-bool ppSubDefineOutput(pmConfig *config, ///< Configuration
-                       const pmFPAview *view ///< View of active readout
+bool ppSubDefineOutput(const char *name,///< Name of output to define
+                       pmConfig *config ///< Configuration
     );
 
 /// Photometry stage 1: measure the PSF from the minuend image
-bool ppSubMakePSF(pmConfig *config,       ///< Configuration
-                  ppSubData *data,    ///< Processing data
-                  const pmFPAview *view ///< View of active readout
+bool ppSubMakePSF(pmConfig *config,     ///< Configuration
+                  ppSubData *data       ///< Processing data
     );
 
 /// Perform the actual image subtraction, update output concepts
-bool ppSubReadoutSubtract(pmConfig *config,       ///< Configuration
-                          const pmFPAview *view ///< View of active readout
+bool ppSubReadoutSubtract(pmConfig *config ///< Configuration
     );
 
 
 /// Photometry stage 2: find and measure sources on the subtracted image
-bool ppSubReadoutPhotometry(pmConfig *config,     ///< Configuration
-                            ppSubData *data,    ///< Processing data
-                            const pmFPAview *view ///< View of active readout
+bool ppSubReadoutPhotometry(const char *name, ///< Name of file to photometer
+                            pmConfig *config, ///< Configuration
+                            ppSubData *data ///< Processing data
     );
 
 /// Renormalize, update headers and generate JPEGs
 bool ppSubReadoutUpdate(pmConfig *config, ///< Configuration
-                        ppSubData *data,    ///< Processing data
-                        const pmFPAview *view ///< View of active readout
+                        ppSubData *data ///< Processing data
     );
 
 /// Higher-order background subtraction
-bool ppSubBackground(pmConfig *config,  ///< Configuration
-                     const pmFPAview *view ///< View to readout
+bool ppSubBackground(pmConfig *config   ///< Configuration
     );
 
@@ -129,4 +127,40 @@
     );
 
+
+/// Activate or deactivate files
+void ppSubFilesActivate(pmConfig *config, // Configuration
+                        ppSubFiles files, // File to activate/deactivate
+                        bool state      // Activation state
+    );
+
+/// Generate a view suitable for a readout
+///
+/// Assumes we're working with skycells
+pmFPAview *ppSubViewReadout(void);
+
+/// Iterate down the FPA hierarchy, opening files
+bool ppSubFilesIterateDown(pmConfig *config, // Configuration
+                           ppSubFiles files // Files to open
+    );
+
+/// Iterate up the FPA hierarchy, closing files
+bool ppSubFilesIterateUp(pmConfig *config, // Configuration
+                         ppSubFiles files // Files to open
+    );
+
+/// Collect statistics
+bool ppSubReadoutStats(pmConfig *config,// Configuration
+                       ppSubData *data  // Processing data
+    );
+
+/// Generate JPEG images
+bool ppSubReadoutJpeg(pmConfig *config  // Configuration
+    );
+
+/// Generate inverse subtraction
+bool ppSubReadoutInverse(pmConfig *config // Configuration
+    );
+
+
 // Copy every instance of a single keyword from one metadata to another
 bool psMetadataCopySingle(psMetadata *target, psMetadata *source, const char *name);
Index: branches/pap/ppSub/src/ppSubArguments.c
===================================================================
--- branches/pap/ppSub/src/ppSubArguments.c	(revision 23711)
+++ branches/pap/ppSub/src/ppSubArguments.c	(revision 23719)
@@ -39,136 +39,4 @@
     psLibFinalize();
     exit(PS_EXIT_CONFIG_ERROR);
-}
-
-// Get a float-point value from the command-line or recipe, and add it to the arguments
-#define VALUE_ARG_RECIPE_FLOAT(ARGNAME, RECIPENAME, TYPE) { \
-    ps##TYPE value = psMetadataLookup##TYPE(NULL, config->arguments, ARGNAME); \
-    if (isnan(value)) { \
-        bool mdok; \
-        value = psMetadataLookup##TYPE(&mdok, recipe, RECIPENAME); \
-        if (!mdok) { \
-            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find %s in recipe %s", \
-                RECIPENAME, PPSUB_RECIPE); \
-            goto ERROR; \
-        } \
-    } \
-    psMetadataAdd##TYPE(recipe, PS_LIST_TAIL, RECIPENAME, PS_META_REPLACE, NULL, value); \
-}
-
-// Get an integer value from the command-line or recipe, and add it to the arguments
-#define VALUE_ARG_RECIPE_INT(ARGNAME, RECIPENAME, TYPE, UNSET) { \
-    ps##TYPE value = psMetadataLookup##TYPE(NULL, config->arguments, ARGNAME); \
-    if (value == UNSET) { \
-        bool mdok; \
-        value = psMetadataLookup##TYPE(&mdok, recipe, RECIPENAME); \
-        if (!mdok) { \
-            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find %s in recipe %s", \
-                RECIPENAME, PPSUB_RECIPE); \
-            goto ERROR; \
-        } \
-    } \
-    psMetadataAdd##TYPE(recipe, PS_LIST_TAIL, RECIPENAME, PS_META_REPLACE, NULL, value); \
-}
-
-/**
- * Get a string value from the command-line and add it to the target
- */
-static bool valueArgStr(psMetadata *arguments, // Command-line arguments
-                        const char *argName, // Argument name in the command-line arguments
-                        const char *mdName, // Name for value in the metadata
-                        psMetadata *target // Target metadata to which to add value
-                        )
-{
-    psString value = psMetadataLookupStr(NULL, arguments, argName); // Value of interest
-    if (value && strlen(value) > 0) {
-        return psMetadataAddStr(target, PS_LIST_TAIL, mdName, PS_META_REPLACE, NULL, value);
-    }
-    return false;
-}
-
-/**
- * Get a string value from the command-line or recipe and add it to the target
- */
-static bool valueArgRecipeStr(psMetadata *arguments, // Command-line arguments
-                              psMetadata *recipe, // Recipe
-                              const char *argName, // Argument name in the command-line arguments
-                              const char *mdName, // Name for value in the metadata and recipe
-                              psMetadata *target // Target metadata to which to add value
-                              )
-{
-    bool mdok;                          // Status of MD lookup
-    psString value = psMetadataLookupStr(&mdok, arguments, argName); // Value of interest
-    if (!value) {
-        value = psMetadataLookupStr(&mdok, recipe, mdName);
-        if (!value) {
-            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find %s in recipe %s",
-                    mdName, PPSUB_RECIPE);
-            return false;
-        }
-    }
-    return psMetadataAddStr(target, PS_LIST_TAIL, mdName, PS_META_REPLACE, NULL, value);
-}
-
-/**
- * Get a vector from the command-line or recipe, and add it to the target
- */
-static bool vectorArgRecipe(psMetadata *arguments, // Command-line arguments
-                            const char *argName, // Argument name in the command-line arguments
-                            const psMetadata *recipe, // Recipe
-                            const char *recipeName, // Name for value in the recipe
-                            psMetadata *target, // Target to which to add value
-                            psElemType type // Type for vector
-    )
-{
-    psVector *vector;                   // Vector
-    psString string = psMetadataLookupStr(NULL, arguments, argName); // String from arguments
-    if (string) {
-        psArray *array = psStringSplitArray(string, ", ", false); // Array of strings
-        vector = psVectorAlloc(array->n, type);
-        for (int i = 0; i < array->n; i++) {
-            const char *subString = array->data[i]; // String with a value
-            char *end;                  // Ptr to end of string parsed
-
-            switch (type) {
-              case PS_TYPE_F32:
-                vector->data.F32[i] = strtof(subString, &end);
-                break;
-              case PS_TYPE_S32: {
-                  long value = strtol(subString, &end, 10);
-                  if (value > PS_MAX_S32 || value < PS_MIN_S32) {
-                      psError(PS_ERR_BAD_PARAMETER_VALUE, true,
-                              "%s list includes value beyond S32 representation: %s",
-                              argName, string);
-                      psFree(vector);
-                      return false;
-                  }
-                  vector->data.S32[i] = value;
-                  break;
-              }
-              default:
-                psAbort("Unsupported type: %x\n", type);
-            }
-            if (end == subString) {
-                psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to decipher %s list: %s",
-                        argName, string);
-                psFree(vector);
-                return false;
-            }
-        }
-        psFree(array);
-    } else {
-        vector = psMetadataLookupPtr(NULL, recipe, recipeName);
-        if (!psMemCheckVector(vector) || vector->type.type != type) {
-            psError(PS_ERR_BAD_PARAMETER_TYPE, false, "%s in recipe %s is not a vector of type F32.",
-                    recipeName, PPSUB_RECIPE);
-            return false;
-        }
-        psMemIncrRefCounter(vector);
-    }
-
-    psMetadataAddVector(target, PS_LIST_TAIL, recipeName, PS_META_REPLACE, NULL, vector);
-    psFree(vector);                     // Drop reference
-
-    return true;
 }
 
@@ -255,5 +123,5 @@
     }
 
-    data->stamps = psMetadataLookupStr(NULL, arguments, "-stamps");
+    data->stamps = psMemIncrRefCounter(psMetadataLookupStr(NULL, arguments, "-stamps"));
 
     const char *statsName = psMetadataLookupStr(NULL, arguments, "-stats"); // Filename for statistics
@@ -281,9 +149,4 @@
     }
 
-    return true;
-}
-
-
-
     psTrace("ppSub", 1, "Done reading command-line arguments\n");
 
Index: branches/pap/ppSub/src/ppSubBackground.c
===================================================================
--- branches/pap/ppSub/src/ppSubBackground.c	(revision 23711)
+++ branches/pap/ppSub/src/ppSubBackground.c	(revision 23719)
@@ -22,8 +22,7 @@
 #include "ppSub.h"
 
-bool ppSubBackground(pmConfig *config, const pmFPAview *view)
+bool ppSubBackground(pmConfig *config)
 {
     psAssert(config, "Require configuration");
-    psAssert(view, "Require view");
 
     bool mdok; // Status of metadata lookups
@@ -36,4 +35,5 @@
     psImageMaskType maskBad = pmConfigMaskGet("BLANK", config); // Bits to mask
 
+    pmFPAview *view = ppSubViewReadout(); // View to readout
     pmReadout *outRO = pmFPAfileThisReadout(config->files, view, "PPSUB.OUTPUT"); // Output image
     pmReadout *modelRO = pmFPAfileThisReadout(config->files, view, "PSPHOT.BACKMDL"); // Background model
@@ -44,4 +44,5 @@
         if (!psphotModelBackground(config, view, "PPSUB.OUTPUT")) {
             psError(PS_ERR_UNKNOWN, false, "Unable to model background");
+            psFree(view);
             return false;
         }
@@ -50,7 +51,10 @@
         if (!modelRO) {
             psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find background model");
+            psFree(view);
             return false;
         }
     }
+    psFree(view);
+
     psImageBinning *binning = psMetadataLookupPtr(&mdok, modelRO->analysis,
                                                   "PSPHOT.BACKGROUND.BINNING"); // Binning for model
Index: branches/pap/ppSub/src/ppSubCamera.c
===================================================================
--- branches/pap/ppSub/src/ppSubCamera.c	(revision 23711)
+++ branches/pap/ppSub/src/ppSubCamera.c	(revision 23719)
@@ -134,5 +134,5 @@
 
 
-bool ppSubCamera(pmConfig *config)
+bool ppSubCamera(pmConfig *config, ppSubData *data)
 {
     psAssert(config, "Require configuration");
@@ -147,5 +147,5 @@
     pmFPAfile *inVar = defineInputFile(config, input, "PPSUB.INPUT.VARIANCE", "INPUT.VARIANCE",
                                        PM_FPA_FILE_VARIANCE);
-    defineInputFile(config, input, "PPSUB.INPUT.SOURCES", "INPUT.SOURCES", PM_FPA_FILE_CMF);
+    defineInputFile(config, NULL, "PPSUB.INPUT.SOURCES", "INPUT.SOURCES", PM_FPA_FILE_CMF);
 
     // Reference image
@@ -158,5 +158,40 @@
     pmFPAfile *refVar = defineInputFile(config, ref, "PPSUB.REF.VARIANCE", "REF.VARIANCE",
                                         PM_FPA_FILE_VARIANCE);
-    defineInputFile(config, ref, "PPSUB.REF.SOURCES", "REF.SOURCES", PM_FPA_FILE_CMF);
+    defineInputFile(config, NULL, "PPSUB.REF.SOURCES", "REF.SOURCES", PM_FPA_FILE_CMF);
+
+
+    // Convolved input image
+    pmFPAfile *inConvImage = defineOutputFile(config, input, true, "PPSUB.INPUT.CONV", PM_FPA_FILE_IMAGE);
+    pmFPAfile *inConvMask = defineOutputFile(config, inConvImage, false, "PPSUB.INPUT.CONV.MASK",
+                                             PM_FPA_FILE_MASK);
+    if (!inConvImage || !inConvMask) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to define output files");
+        return false;
+    }
+    if (inVar) {
+        pmFPAfile *inConvVar = defineOutputFile(config, inConvImage, false, "PPSUB.INPUT.CONV.VARIANCE",
+                                                PM_FPA_FILE_VARIANCE);
+        if (!inConvVar) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to define output files");
+            return false;
+        }
+    }
+
+    // Convolved ref image
+    pmFPAfile *refConvImage = defineOutputFile(config, input, true, "PPSUB.REF.CONV", PM_FPA_FILE_IMAGE);
+    pmFPAfile *refConvMask = defineOutputFile(config, refConvImage, false, "PPSUB.REF.CONV.MASK",
+                                              PM_FPA_FILE_MASK);
+    if (!refConvImage || !refConvMask) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to define output files");
+        return false;
+    }
+    if (refVar) {
+        pmFPAfile *refConvVar = defineOutputFile(config, refConvImage, false, "PPSUB.REF.CONV.VARIANCE",
+                                                 PM_FPA_FILE_VARIANCE);
+        if (!refConvVar) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to define output files");
+            return false;
+        }
+    }
 
 
@@ -172,5 +207,5 @@
 
     // Output image
-    pmFPAfile *output = defineOutputFile(config, input, true, "PPSUB.OUTPUT", PM_FPA_FILE_IMAGE);
+    pmFPAfile *output = defineOutputFile(config, inConvImage, true, "PPSUB.OUTPUT", PM_FPA_FILE_IMAGE);
     pmFPAfile *outMask = defineOutputFile(config, output, false, "PPSUB.OUTPUT.MASK", PM_FPA_FILE_MASK);
     if (!output || !outMask) {
@@ -190,10 +225,11 @@
     }
 
+    pmFPAfile *inverse = NULL;          // Inverse output image
     if (data->inverse) {
         // Inverse output image
-        pmFPAfile *inverse = defineOutputFile(config, input, true, "PPSUB.INV", PM_FPA_FILE_IMAGE);
+        inverse = defineOutputFile(config, output, true, "PPSUB.INVERSE", PM_FPA_FILE_IMAGE);
         pmFPAfile *invMask = defineOutputFile(config, inverse, false, "PPSUB.INVERSE.MASK",
                                               PM_FPA_FILE_MASK);
-        if (!inv || !invMask) {
+        if (!inverse || !invMask) {
             psError(PS_ERR_UNKNOWN, false, "Unable to define output files");
             return false;
@@ -212,38 +248,4 @@
     }
 
-    // Convolved input image
-    pmFPAfile *inConvImage = defineOutputFile(config, input, true, "PPSUB.INPUT.CONV", PM_FPA_FILE_IMAGE);
-    pmFPAfile *inConvMask = defineOutputFile(config, inConvImage, false, "PPSUB.INPUT.CONV.MASK",
-                                             PM_FPA_FILE_MASK);
-    if (!inConvImage || !inConvMask) {
-        psError(PS_ERR_UNKNOWN, false, "Unable to define output files");
-        return false;
-    }
-    if (inVar) {
-        pmFPAfile *inConvVar = defineOutputFile(config, inConvImage, false, "PPSUB.INPUT.CONV.VARIANCE",
-                                                PM_FPA_FILE_VARIANCE);
-        if (!inConvVar) {
-            psError(PS_ERR_UNKNOWN, false, "Unable to define output files");
-            return false;
-        }
-    }
-
-    // Convolved ref image
-    pmFPAfile *refConvImage = defineOutputFile(config, input, true, "PPSUB.REF.CONV", PM_FPA_FILE_IMAGE);
-    pmFPAfile *refConvMask = defineOutputFile(config, refConvImage, false, "PPSUB.REF.CONV.MASK",
-                                              PM_FPA_FILE_MASK);
-    if (!refConvImage || !refConvMask) {
-        psError(PS_ERR_UNKNOWN, false, "Unable to define output files");
-        return false;
-    }
-    if (refVar) {
-        pmFPAfile *refConvVar = defineOutputFile(config, refConvImage, false, "PPSUB.REF.CONV.VARIANCE",
-                                                 PM_FPA_FILE_VARIANCE);
-        if (!refConvVar) {
-            psError(PS_ERR_UNKNOWN, false, "Unable to define output files");
-            return false;
-        }
-    }
-
 
     // Output JPEGs
@@ -276,26 +278,8 @@
     }
 
-    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSUB_RECIPE); // Recipe for ppSim
-    if (!recipe) {
-        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find recipe %s", PPSUB_RECIPE);
-        return false;
-    }
-
     // psPhot input
     if (data->photometry) {
         psphotModelClassInit();        // load implementation-specific models
 
-        // Internal-ish file for getting the PSF from the minuend
-        pmFPAfile *psf = pmFPAfileDefineOutputFromFile(config, output, "PSPHOT.PSF.LOAD");
-        if (!psf) {
-            psError(PS_ERR_IO, false, "Failed to build FPA from PSPHOT.PSF.LOAD");
-            return false;
-        }
-        if (psf->type != PM_FPA_FILE_PSF) {
-            psError(PS_ERR_IO, true, "PSPHOT.PSF.LOAD is not of type PSF");
-            return false;
-        }
-        pmFPAfileActivate(config->files, false, "PSPHOT.PSF.LOAD");
-
         pmFPAfile *psphot = pmFPAfileDefineFromFPA(config, output->fpa, 1, 1, "PSPHOT.INPUT");
         if (!psphot) {
@@ -307,4 +291,17 @@
             return false;
         }
+        pmFPAfileActivate(config->files, false, "PSPHOT.INPUT");
+
+        // Internal-ish file for getting the PSF from the minuend
+        pmFPAfile *psf = pmFPAfileDefineOutputFromFile(config, psphot, "PSPHOT.PSF.LOAD");
+        if (!psf) {
+            psError(PS_ERR_IO, false, "Failed to build FPA from PSPHOT.PSF.LOAD");
+            return false;
+        }
+        if (psf->type != PM_FPA_FILE_PSF) {
+            psError(PS_ERR_IO, true, "PSPHOT.PSF.LOAD is not of type PSF");
+            return false;
+        }
+        pmFPAfileActivate(config->files, false, "PSPHOT.PSF.LOAD");
 
         if (!psphotDefineFiles(config, psphot)) {
@@ -326,5 +323,5 @@
 
         if (data->inverse) {
-            pmFPAfile *invSources = defineOutputFile(config, inverse, false, "PPSUB.OUTPUT.SOURCES",
+            pmFPAfile *invSources = defineOutputFile(config, inverse, false, "PPSUB.INVERSE.SOURCES",
                                                      PM_FPA_FILE_CMF);
             if (!invSources) {
@@ -332,6 +329,6 @@
                 return false;
             }
-        }
-        invSources->save = true;
+            invSources->save = true;
+        }
     }
 
Index: branches/pap/ppSub/src/ppSubData.c
===================================================================
--- branches/pap/ppSub/src/ppSubData.c	(revision 23711)
+++ branches/pap/ppSub/src/ppSubData.c	(revision 23719)
@@ -9,13 +9,4 @@
 
 #include "ppSub.h"
-
-
-// Image files to activate/deactivate
-static const char *imageFiles[] = { "PPSUB.OUTPUT", "PPSUB.OUTPUT.MASK", "PPSUB.OUTPUT.VARIANCE",
-                                    "PPSUB.OUTPUT.KERNELS", "PPSUB.OUTPUT.JPEG1", "PPSUB.OUTPUT.JPEG2",
-                                    "PPSUB.INPUT.CONV", "PPSUB.INPUT.CONV.MASK", "PPSUB.INPUT.CONV.VARIANCE",
-                                    "PPSUB.REF.CONV", "PPSUB.REF.CONV.MASK", "PPSUB.REF.CONV.VARIANCE",
-                                    NULL };
-
 
 
@@ -32,4 +23,7 @@
         fclose(data->statsFile);
     }
+    psFree(data->stamps);
+    psFree(data->psf);
+    psFree(data->statsFile);
     psFree(data->stats);
     return;
@@ -44,9 +38,11 @@
     data->photometry = false;
     data->inverse = false;
+    data->stamps = NULL;
+    data->psf = NULL;
     data->statsFile = NULL;
     data->stats = psMetadataAlloc();
     psMetadataAddS32(data->stats, PS_LIST_TAIL, "QUALITY", 0, "Data quality", 0);
 
-    return options;
+    return data;
 }
 
@@ -62,12 +58,5 @@
     }
 
-    if (files & PPSUB_FILES_IMAGE) {
-        for (int i = 0; imageFiles[i]; i++) {
-            pmFPAfileActivate(config->files, imageFiles[i], false);
-        }
-    }
-    if (files & PPSUB_FILES_PHOT) {
-        psphotFilesActivate(config, false);
-    }
+    ppSubFilesActivate(config, files, false);
 
     psErrorClear();
Index: branches/pap/ppSub/src/ppSubDefineOutput.c
===================================================================
--- branches/pap/ppSub/src/ppSubDefineOutput.c	(revision 23711)
+++ branches/pap/ppSub/src/ppSubDefineOutput.c	(revision 23719)
@@ -21,10 +21,10 @@
 #include "ppSub.h"
 
-bool ppSubDefineOutput(const char *name, pmConfig *config, const ppSubData *data, const pmFPAview *view)
+bool ppSubDefineOutput(const char *name, pmConfig *config)
 {
     psAssert(name, "Require name");
     psAssert(config, "Require configuration");
-    psAssert(view, "Require view");
 
+    pmFPAview *view = ppSubViewReadout(); // View to readout
     pmCell *outCell = pmFPAfileThisCell(config->files, view, name); // Output cell
     pmFPA *outFPA = outCell->parent->parent; // Output FPA
@@ -45,4 +45,5 @@
     pmReadout *inConv = pmFPAfileThisReadout(config->files, view, "PPSUB.INPUT.CONV"); // Input readout
     pmReadout *refConv = pmFPAfileThisReadout(config->files, view, "PPSUB.REF.CONV"); // Reference readout
+    psFree(view);
 
     // Add kernel descrption to header.
Index: branches/pap/ppSub/src/ppSubErrorCodes.c.in
===================================================================
--- branches/pap/ppSub/src/ppSubErrorCodes.c.in	(revision 23711)
+++ branches/pap/ppSub/src/ppSubErrorCodes.c.in	(revision 23719)
@@ -1,7 +1,7 @@
-/** @file pswarpErrorCodes.c.in
+/** @file ppSubErrorCodes.c.in
  *
  *  @brief
  *
- *  @ingroup pswarp
+ *  @ingroup ppSub
  *
  *  @author IfA
@@ -12,19 +12,19 @@
 
 #include "pslib.h"
-#include "pswarpErrorCodes.h"
+#include "ppSubErrorCodes.h"
 
 /*
  * The line
-    { PSWARP_ERR_$X{ErrorCode}, "$X{ErrorDescription}"},
+    { PPSUB_ERR_$X{ErrorCode}, "$X{ErrorDescription}"},
  * (without the Xs)
  * will be replaced by values from errorCodes.dat
  */
-void pswarpErrorRegister(void)
+void ppSubErrorRegister(void)
 {
     static psErrorDescription errors[] = {
-       { PSWARP_ERR_BASE, "First value we use; lower values belong to psLib" },
-       { PSWARP_ERR_${ErrorCode}, "${ErrorDescription}"},
+       { PPSUB_ERR_BASE, "First value we use; lower values belong to psLib" },
+       { PPSUB_ERR_${ErrorCode}, "${ErrorDescription}"},
     };
-    static int nerror = PSWARP_ERR_NERROR - PSWARP_ERR_BASE; ///< number of values in enum
+    static int nerror = PPSUB_ERR_NERROR - PPSUB_ERR_BASE; ///< number of values in enum
 
     for (int i = 0; i < nerror; i++) {
Index: branches/pap/ppSub/src/ppSubErrorCodes.h.in
===================================================================
--- branches/pap/ppSub/src/ppSubErrorCodes.h.in	(revision 23711)
+++ branches/pap/ppSub/src/ppSubErrorCodes.h.in	(revision 23719)
@@ -1,7 +1,7 @@
-/** @file pswarpErrorCodes.h.in
+/** @file ppSubErrorCodes.h.in
  *
  *  @brief
  *
- *  @ingroup pswarp
+ *  @ingroup ppSub
  *
  *  @author IfA
@@ -11,9 +11,9 @@
  */
 
-#if !defined(PSWARP_ERROR_CODES_H)
-#define PSWARP_ERROR_CODES_H
+#if !defined(PPSUB_ERROR_CODES_H)
+#define PPSUB_ERROR_CODES_H
 /*
  * The line
- *  PSWARP_ERR_$X{ErrorCode},
+ *  PPSUB_ERR_$X{ErrorCode},
  * (without the X)
  *
@@ -21,10 +21,10 @@
  */
 typedef enum {
-    PSWARP_ERR_BASE = 14000,
-    PSWARP_ERR_${ErrorCode},
-    PSWARP_ERR_NERROR
-} pswarpErrorCode;
+    PPSUB_ERR_BASE = 14000,
+    PPSUB_ERR_${ErrorCode},
+    PPSUB_ERR_NERROR
+} ppSubErrorCode;
 
-void pswarpErrorRegister(void);
+void ppSubErrorRegister(void);
 
 #endif
Index: branches/pap/ppSub/src/ppSubFiles.c
===================================================================
--- branches/pap/ppSub/src/ppSubFiles.c	(revision 23711)
+++ branches/pap/ppSub/src/ppSubFiles.c	(revision 23719)
@@ -12,7 +12,7 @@
 
 // Input files
-static const char *inputFiles[] = { "PPSUB.OUTPUT", "PPSUB.OUTPUT.MASK", "PPSUB.OUTPUT.VARIANCE",
-                                    "PPSUB.OUTPUT.KERNELS", "PPSUB.OUTPUT.JPEG1", "PPSUB.OUTPUT.JPEG2",
-                                    NULL };
+static const char *inputFiles[] = { "PPSUB.INPUT", "PPSUB.INPUT.MASK", "PPSUB.INPUT.VARIANCE",
+                                    "PPSUB.INPUT.SOURCES", "PPSUB.REF", "PPSUB.REF.MASK",
+                                    "PPSUB.REF.VARIANCE", "PPSUB.REF.SOURCES", NULL };
 
 // Convolved files
@@ -23,5 +23,5 @@
 // Subtraction files
 static const char *subFiles[] = { "PPSUB.OUTPUT", "PPSUB.OUTPUT.MASK", "PPSUB.OUTPUT.VARIANCE",
-                                  "PPSUB.OUTPUT.KERNELS", "PPSUB.OUTPUT.JPEG1", "PPSUB.OUTPUT.JPEG2",
+                                  "PPSUB.OUTPUT.JPEG1", "PPSUB.OUTPUT.JPEG2",
                                   NULL };
 
@@ -30,10 +30,11 @@
 
 // Inverse subtraction files
-static const char *invFiles[] = { "PPSUB.OUTINV", "PPSUB.OUTINV.MASK", "PPSUB.OUTINV.VARIANCE",
-                                  "PPSUB.OUTINV.KERNELS", "PPSUB.OUTINV.JPEG1", "PPSUB.OUTINV.JPEG2",
-                                  NULL };
+static const char *invFiles[] = { "PPSUB.INVERSE", "PPSUB.INVERSE.MASK", "PPSUB.INVERSE.VARIANCE", NULL };
 
 // Inverse subtraction photometry
-static const char *subPhotFiles[] = { "PPSUB.OUTINV.SOURCES", NULL };
+static const char *invPhotFiles[] = { "PPSUB.INVERSE.SOURCES", NULL };
+
+// PSF files
+static const char *psfFiles[] = { "PSPHOT.PSF.SAVE", NULL };
 
 // Calculation (may be either input or output) files
@@ -47,6 +48,6 @@
     )
 {
-    for (int i = 0; imageFiles[i]; i++) {
-        pmFPAfileActivate(config->files, files[i], state);
+    for (int i = 0; files[i]; i++) {
+        pmFPAfileActivate(config->files, state, files[i]);
     }
     return;
@@ -62,8 +63,8 @@
     )
 {
-    for (int i = 0; imageFiles[i]; i++) {
+    for (int i = 0; files[i]; i++) {
         pmFPAfile *file = pmFPAfileSelectSingle(config->files, files[i], 0);
         if (file && file->save == save) {
-            pmFPAfileActivate(config->files, files[i], state);
+            pmFPAfileActivate(config->files, state, files[i]);
         }
     }
@@ -76,5 +77,5 @@
 
     if (files & PPSUB_FILES_INPUT) {
-        filesActivate(inputFiles, state);
+        filesActivate(config, inputFiles, state);
         filesActivateSave(config, calcFiles, false, state);
     }
@@ -95,6 +96,9 @@
         filesActivate(config, invPhotFiles, state);
     }
+    if (files & PPSUB_FILES_PSF) {
+        filesActivate(config, psfFiles, state);
+    }
     if (files & PPSUB_FILES_PHOT) {
-        psphotFilesActivate(config, false);
+        psphotFilesActivate(config, state);
     }
 
@@ -110,7 +114,10 @@
 }
 
-bool ppSubFilesIterateDown(pmConfig *config)
+bool ppSubFilesIterateDown(pmConfig *config, ppSubFiles files)
 {
     psAssert(config, "Require configuration");
+
+    ppSubFilesActivate(config, PPSUB_FILES_ALL, false);
+    ppSubFilesActivate(config, files, true);
 
     pmFPAview *view = pmFPAviewAlloc(0);// View to FPA top
@@ -139,10 +146,15 @@
     }
 
+    ppSubFilesActivate(config, PPSUB_FILES_ALL, false);
+
     return true;
 }
 
-bool ppSubFilesIterateUp(pmConfig *config)
+bool ppSubFilesIterateUp(pmConfig *config, ppSubFiles files)
 {
     psAssert(config, "Require configuration");
+
+    ppSubFilesActivate(config, PPSUB_FILES_ALL, false);
+    ppSubFilesActivate(config, files, true);
 
     pmFPAview *view = ppSubViewReadout(); // View to readout
@@ -171,4 +183,6 @@
     }
 
+    ppSubFilesActivate(config, PPSUB_FILES_ALL, false);
+
     return true;
 }
Index: branches/pap/ppSub/src/ppSubLoop.c
===================================================================
--- branches/pap/ppSub/src/ppSubLoop.c	(revision 23711)
+++ branches/pap/ppSub/src/ppSubLoop.c	(revision 23719)
@@ -17,5 +17,4 @@
 #include <pslib.h>
 #include <psmodules.h>
-#include <ppStats.h>
 
 #include "ppSub.h"
@@ -70,15 +69,15 @@
     }
 
-    if (!ppSubDefineOutput("PPSUB.OUTPUT", config, data, view)) {
+    if (!ppSubDefineOutput("PPSUB.OUTPUT", config)) {
         psError(PS_ERR_UNKNOWN, false, "Unable to define output.");
         return false;
     }
 
-    if (!data->quality && !ppSubMakePSF("PPSUB.OUTPUT", "PPSUB.INVERSE", config, data, view)) {
+    if (!data->quality && !ppSubMakePSF(config, data)) {
         psError(PS_ERR_UNKNOWN, false, "Unable to generate PSF.");
         return false;
     }
 
-    if (!ppSubReadoutSubtract("PPSUB.OUTPUT", config, view)) {
+    if (!ppSubReadoutSubtract(config)) {
         psError(PS_ERR_UNKNOWN, false, "Unable to subtract images.");
         return false;
@@ -86,5 +85,5 @@
 
     // Close convolved files
-    if (!ppSubFilesIterateUp(config, PPSUB_FILES_CONV)) {
+    if (!ppSubFilesIterateUp(config, PPSUB_FILES_PSF | PPSUB_FILES_CONV)) {
         psError(PPSUB_ERR_IO, false, "Unable to close input files.");
         return false;
@@ -92,21 +91,31 @@
 
     // Higher order background subtraction using psphot
-    if (!ppSubBackground(config, view)) {
+    if (!ppSubBackground(config)) {
         psError(PS_ERR_UNKNOWN, false, "Unable to subtract background.");
         return false;
     }
 
-    if (!data->quality && ppSubReadoutPhotometry("PPSUB.OUTPUT", PPSUB_FILES_PHOT_SUB, config, data, view)) {
+    if (!ppSubFilesIterateDown(config, PPSUB_FILES_PHOT_SUB)) {
+        psError(PPSUB_ERR_IO, false, "Unable to set up photometry files.");
+        return false;
+    }
+
+    if (!data->quality && !ppSubReadoutPhotometry("PPSUB.OUTPUT", config, data)) {
         psError(PS_ERR_UNKNOWN, false, "Unable to perform photometry.");
         return false;
     }
 
+    if (!ppSubFilesIterateUp(config, PPSUB_FILES_PHOT_SUB)) {
+        psError(PPSUB_ERR_IO, false, "Unable to set up photometry files.");
+        return false;
+    }
+
     // Perform statistics on the cell
-    if (!ppSubReadoutStats(config, data, view)) {
+    if (!ppSubReadoutStats(config, data)) {
         psError(PS_ERR_UNKNOWN, false, "Unable to collect statistics");
         return false;
     }
 
-    if (!ppSubReadoutJpeg("PPSUB.OUTPUT", config, data, view)) {
+    if (!ppSubReadoutJpeg(config)) {
         psError(PS_ERR_UNKNOWN, false, "Unable to update.");
         return false;
@@ -120,10 +129,10 @@
         }
 
-        if (data->inverse && !ppSubDefineOutput("PPSUB.INVERSE", config, data, view)) {
+        if (data->inverse && !ppSubDefineOutput("PPSUB.INVERSE", config)) {
             psError(PS_ERR_UNKNOWN, false, "Unable to define inverse.");
             return false;
         }
 
-        if (!ppSubReadoutInverse("PPSUB.INVERSE", "PPSUB.OUTPUT", config, view)) {
+        if (!ppSubReadoutInverse(config)) {
             psError(PS_ERR_UNKNOWN, false, "Unable to invert images.");
             return false;
@@ -136,6 +145,10 @@
         }
 
-        if (!data->quality && data->!ppSubReadoutPhotometry("PPSUB.INVERSE", PPSUB_FILES_PHOT_INV,
-                                                            config, data, view)) {
+        if (!ppSubFilesIterateDown(config, PPSUB_FILES_PHOT_INV)) {
+            psError(PPSUB_ERR_IO, false, "Unable to set up inverse files.");
+            return false;
+        }
+
+        if (!data->quality && !ppSubReadoutPhotometry("PPSUB.INVERSE", config, data)) {
             psError(PS_ERR_UNKNOWN, false, "Unable to perform photometry.");
             return false;
@@ -143,5 +156,5 @@
 
         // Close inverse subtraction files
-        if (!ppSubFilesIterateUp(config, PPSUB_FILES_INV)) {
+        if (!ppSubFilesIterateUp(config, PPSUB_FILES_INV | PPSUB_FILES_PHOT_INV)) {
             psError(PPSUB_ERR_IO, false, "Unable to close subtraction files.");
             return false;
@@ -155,7 +168,5 @@
     }
 
-    psFree(view);
-
-    psString dump_file = psMetadataLookupStr(&mdok, config->arguments, "-dumpconfig");
+    psString dump_file = psMetadataLookupStr(NULL, config->arguments, "-dumpconfig");
     if (dump_file) {
         pmFPAfile *input = psMetadataLookupPtr(NULL, config->files, "PPSUB.INPUT"); // Input file
@@ -164,6 +175,3 @@
 
     return true;
-
-ERROR:
-    return false;
 }
Index: branches/pap/ppSub/src/ppSubMakePSF.c
===================================================================
--- branches/pap/ppSub/src/ppSubMakePSF.c	(revision 23711)
+++ branches/pap/ppSub/src/ppSubMakePSF.c	(revision 23719)
@@ -22,8 +22,11 @@
 #include "ppSub.h"
 
-bool ppSubMakePSF(pmConfig *config, ppSubData *data, const pmFPAview *view)
+bool ppSubMakePSF(pmConfig *config, ppSubData *data)
 {
     psAssert(config, "Require configuration");
-    psAssert(view, "Require view");
+
+    if (!data->photometry) {
+        return true;
+    }
 
     psTimerStart("PPSUB_PHOT");
@@ -44,4 +47,5 @@
     pmReadout *minuend = NULL;          // Image that will be positive following subtraction
     pmFPAfile *minuendFile = NULL;      // File for minuend image
+    pmFPAview *view = ppSubViewReadout(); // View to readout
     if (reverse) {
         minuend = pmFPAfileThisReadout(config->files, view, "PPSUB.REF.CONV");
@@ -53,7 +57,8 @@
 
 #if 1
+    pmFPAfile *photFile = psMetadataLookupPtr(&mdok, config->files, "PSPHOT.INPUT"); // Photometry file
+#if 0
+    pmReadout *photRO = pmFPAviewThisReadout(view, photFile->fpa); // Readout to photometer
     pmReadout *template = minuend;
-    pmFPAfile *photFile = psMetadataLookupPtr(&mdok, config->files, "PSPHOT.INPUT"); // Photometry file
-    pmReadout *photRO = pmFPAviewThisReadout(view, photFile->fpa); // Readout to photometer
     if (!photRO) {
         pmCell *cell = pmFPAviewThisCell(view, photFile->fpa); // Cell to photometer
@@ -74,4 +79,17 @@
     }
 #else
+    if (!pmFPACopy(photFile->fpa, minuendFile->fpa)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to copy FPA for photometry");
+        psFree(view);
+        return false;
+    }
+    pmReadout *photRO = pmFPAviewThisReadout(view, photFile->fpa); // Readout to photometer
+    if (psMetadataLookup(photRO->analysis, "PSPHOT.SOURCES")) {
+        psMetadataRemoveKey(photRO->analysis, "PSPHOT.SOURCES");
+    }
+#endif
+
+
+#else
     // Supply the minuend pmFPAfile to psphot as PSPHOT.INPUT:
     psMetadataAddPtr(config->files, PS_LIST_TAIL, "PSPHOT.INPUT", PS_DATA_UNKNOWN | PS_META_REPLACE,
@@ -87,5 +105,6 @@
         psErrorStackPrint(stderr, "Unable to determine PSF");
         psWarning("Unable to determine PSF --- suspect bad data quality.");
-        ppSubDataQuality(config, data, PSPHOT_ERR_PSF, PPSUB_FILES_PHOT);
+        ppSubDataQuality(config, data, PSPHOT_ERR_PSF, PPSUB_FILES_PHOT_SUB | PPSUB_FILES_PHOT_INV);
+        psFree(view);
         return true;
     }
@@ -93,4 +112,5 @@
     // Record the FWHM in the output header
     pmReadout *outRO = pmFPAfileThisReadout(config->files, view, "PPSUB.OUTPUT"); // Output readout
+    psFree(view);
     pmHDU *hdu = pmHDUFromCell(outRO->parent); // HDU with header
     psMetadataItemSupplement(hdu->header, psphotRecipe, "FWHM_MAJ");
@@ -100,4 +120,7 @@
     psMetadataRemoveKey(photRO->analysis, "PSPHOT.HEADER");
 
+    data->psf = psMemIncrRefCounter(psMetadataLookupPtr(NULL, photRO->parent->parent->analysis,
+                                                        "PSPHOT.PSF"));
+
     return true;
 }
Index: branches/pap/ppSub/src/ppSubMatchPSFs.c
===================================================================
--- branches/pap/ppSub/src/ppSubMatchPSFs.c	(revision 23711)
+++ branches/pap/ppSub/src/ppSubMatchPSFs.c	(revision 23719)
@@ -22,12 +22,13 @@
 #include "ppSub.h"
 
-bool ppSubMatchPSFs(pmConfig *config, ppSubData *data, const pmFPAview *view)
+bool ppSubMatchPSFs(pmConfig *config, ppSubData *data)
 {
     psAssert(config, "Require configuration");
-    psAssert(view, "Require view");
 
     // Look up recipe values
     psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSUB_RECIPE); // Recipe for ppSim
     psAssert(recipe, "We checked this earlier, so it should be here.");
+
+    pmFPAview *view = ppSubViewReadout(); // View to readout
 
     // Input images
@@ -52,7 +53,11 @@
     pmReadout *kernelRO = pmFPAfileThisReadout(config->files, view, "PPSUB.OUTPUT.KERNELS"); // RO with kernel
 
+    psFree(view);
+
     // Sources in image, used for stamps: these must be loaded from previous analysis stages
-    psArray *inSources = psMetadataLookupPtr(&mdok, inRO->analysis, "PSPHOT.SOURCES"); // Input source list
-    psArray *refSources = psMetadataLookupPtr(&mdok, refRO->analysis, "PSPHOT.SOURCES"); // Ref source list
+    pmReadout *inSourceRO = pmFPAfileThisReadout(config->files, view, "PPSUB.INPUT.SOURCES");
+    pmReadout *refSourceRO = pmFPAfileThisReadout(config->files, view, "PPSUB.REF.SOURCES");
+    psArray *inSources = psMetadataLookupPtr(&mdok, inSourceRO->analysis, "PSPHOT.SOURCES"); // Source list
+    psArray *refSources = psMetadataLookupPtr(&mdok, refSourceRO->analysis, "PSPHOT.SOURCES"); // Source list
 
     psArray *sources = NULL;            // Merged list of sources
Index: branches/pap/ppSub/src/ppSubReadoutInverse.c
===================================================================
--- branches/pap/ppSub/src/ppSubReadoutInverse.c	(revision 23719)
+++ branches/pap/ppSub/src/ppSubReadoutInverse.c	(revision 23719)
@@ -0,0 +1,27 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppSub.h"
+
+
+bool ppSubReadoutInverse(pmConfig *config)
+{
+    psAssert(config, "Require configuration");
+
+    pmFPAview *view = ppSubViewReadout(); // View to readout
+    pmReadout *outRO = pmFPAfileThisReadout(config->files, view, "PPSUB.OUTPUT");
+    pmReadout *invRO = pmFPAfileThisReadout(config->files, view, "PPSUB.INVERSE");
+
+    invRO->image = (psImage*)psBinaryOp(invRO->image, outRO->image, "*", psScalarAlloc(-1.0, PS_TYPE_F32));
+    invRO->mask = psMemIncrRefCounter(outRO->mask);
+    invRO->variance = psMemIncrRefCounter(outRO->variance);
+    invRO->covariance = psMemIncrRefCounter(outRO->covariance);
+
+    invRO->data_exists = invRO->parent->data_exists = invRO->parent->parent->data_exists = true;
+
+    return true;
+}
Index: branches/pap/ppSub/src/ppSubReadoutJpeg.c
===================================================================
--- branches/pap/ppSub/src/ppSubReadoutJpeg.c	(revision 23711)
+++ branches/pap/ppSub/src/ppSubReadoutJpeg.c	(revision 23719)
@@ -9,42 +9,41 @@
 #include "ppSub.h"
 
-bool ppSubReadoutJpeg(pmConfig *config, ppSubData *data, const pmFPAview *view)
+bool ppSubReadoutJpeg(pmConfig *config)
 {
     psAssert(config, "Require configuration");
-    psAssert(view, "Require view");
 
-    bool mdok = false;                  // Status of MD lookup
+    pmFPAview *view = ppSubViewReadout(); // View to readout
+    pmReadout *outRO = pmFPAfileThisReadout(config->files, view, "PPSUB.OUTPUT"); // Readout to jpeg
 
     // Look up recipe values
-    psMetadata *recipe = psMetadataLookupMetadata(&mdok, config->recipes, PPSUB_RECIPE); // Recipe for ppSim
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSUB_RECIPE); // Recipe for ppSim
     psAssert(recipe, "We checked this earlier, so it should be here.");
 
     // Generate binned JPEGs
-    {
-        psImageMaskType maskBad = pmConfigMaskGet("BLANK", config); // Bits to mask for bad pixels
+    psImageMaskType maskBad = pmConfigMaskGet("BLANK", config); // Bits to mask for bad pixels
 
-        int bin1 = psMetadataLookupS32(NULL, recipe, "BIN1"); // First binning level
-        int bin2 = psMetadataLookupS32(NULL, recipe, "BIN2"); // Second binning level
+    int bin1 = psMetadataLookupS32(NULL, recipe, "BIN1"); // First binning level
+    int bin2 = psMetadataLookupS32(NULL, recipe, "BIN2"); // Second binning level
 
-        // Target cells
-        pmCell *cell1 = pmFPAfileThisCell(config->files, view, "PPSUB.OUTPUT.JPEG1"); // Rebinned cell once
-        pmCell *cell2 = pmFPAfileThisCell(config->files, view, "PPSUB.OUTPUT.JPEG2"); // Rebinned cell twice
+    // Target cells
+    pmCell *cell1 = pmFPAfileThisCell(config->files, view, "PPSUB.OUTPUT.JPEG1"); // Rebinned cell once
+    pmCell *cell2 = pmFPAfileThisCell(config->files, view, "PPSUB.OUTPUT.JPEG2"); // Rebinned cell twice
+    psFree(view);
 
-        pmReadout *ro1 = pmReadoutAlloc(cell1), *ro2 = pmReadoutAlloc(cell2); // Binned readouts
-        if (!pmReadoutRebin(ro1, outRO, maskBad, bin1, bin1)) {
-            psError(PS_ERR_UNKNOWN, false, "Unable to bin output (1st binning)");
-            psFree(ro1);
-            psFree(ro2);
-            return false;
-        }
-        if (!pmReadoutRebin(ro2, ro1, 0, bin2, bin2)) {
-            psError(PS_ERR_UNKNOWN, false, "Unable to bin output (2nd binning)");
-            psFree(ro1);
-            psFree(ro2);
-            return false;
-        }
+    pmReadout *ro1 = pmReadoutAlloc(cell1), *ro2 = pmReadoutAlloc(cell2); // Binned readouts
+    if (!pmReadoutRebin(ro1, outRO, maskBad, bin1, bin1)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to bin output (1st binning)");
         psFree(ro1);
         psFree(ro2);
+        return false;
     }
+    if (!pmReadoutRebin(ro2, ro1, 0, bin2, bin2)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to bin output (2nd binning)");
+        psFree(ro1);
+        psFree(ro2);
+        return false;
+    }
+    psFree(ro1);
+    psFree(ro2);
 
     return true;
Index: branches/pap/ppSub/src/ppSubReadoutPhotometry.c
===================================================================
--- branches/pap/ppSub/src/ppSubReadoutPhotometry.c	(revision 23711)
+++ branches/pap/ppSub/src/ppSubReadoutPhotometry.c	(revision 23719)
@@ -22,8 +22,11 @@
 #include "ppSub.h"
 
-bool ppSubReadoutPhotometry (pmConfig *config, ppSubData *data, const pmFPAview *view)
+bool ppSubReadoutPhotometry(const char *name, pmConfig *config, ppSubData *data)
 {
     psAssert(config, "Require configuration");
-    psAssert(view, "Require view");
+
+    if (!data->photometry) {
+        return false;
+    }
 
     // Look up recipe values
@@ -39,4 +42,5 @@
     // The PSF (measured in ppSubMakePSF) is stored on the chip->analysis of PSPHOT.INPUT
     // In order to use an incoming PSF, it must be stored on the chip->analysis of PSPHOT.PSF.LOAD
+    pmFPAview *view = ppSubViewReadout(); // View to readout
     pmChip *psfInputChip = pmFPAfileThisChip(config->files, view, "PSPHOT.INPUT"); // Chip with PSF
     psAssert (psfInputChip, "should have been generated for ppSubMakePSF");
@@ -47,5 +51,5 @@
         psErrorStackPrint(stderr, "No PSF available");
         psWarning("No PSF available --- suspect bad data quality.");
-        ppSubDataQuality(config, data, psErrorCodeLast(), PPSUB_FILES_PHOT);
+        ppSubDataQuality(config, data, psErrorCodeLast(), PPSUB_FILES_PHOT_SUB | PPSUB_FILES_PHOT_INV);
         return true;
     }
@@ -58,8 +62,6 @@
     // around the pointers so PSPHOT.INPUT corresponds to the output image; previously, it was
     // equivalent to the minuend image.
-    pmFPAfile *outputFile = psMetadataLookupPtr(&mdok, config->files, "PPSUB.OUTPUT"); // Output file
-    pmReadout *outRO = pmFPAviewThisReadout(view, outputFile->fpa); // Readout with the sources
+    pmReadout *inRO = pmFPAfileThisReadout(config->files, view, name); // Readout with image and sources
 
-    // XXX possibly rename this to PPSUB.RESID?
     pmFPAfile *photFile = psMetadataLookupPtr(&mdok, config->files, "PSPHOT.INPUT"); // Photometry file
     pmReadout *photRO = pmFPAviewThisReadout(view, photFile->fpa); // Readout to photometer
@@ -68,17 +70,22 @@
         photRO = pmReadoutAlloc(cell); // Output readout: subtraction
     }
-    photRO->image = psImageCopy(photRO->image, outRO->image, PS_TYPE_F32);
-    if (outRO->variance) {
-        photRO->variance = psImageCopy(photRO->variance, outRO->variance, PS_TYPE_F32);
+    photRO->image = psImageCopy(photRO->image, inRO->image, PS_TYPE_F32);
+    if (inRO->variance) {
+        photRO->variance = psImageCopy(photRO->variance, inRO->variance, PS_TYPE_F32);
     } else {
         psFree(photRO->variance);
         photRO->variance = NULL;
     }
-    if (outRO->mask) {
-        photRO->mask = psImageCopy(photRO->mask, outRO->mask, PS_TYPE_IMAGE_MASK);
+    if (inRO->mask) {
+        photRO->mask = psImageCopy(photRO->mask, inRO->mask, PS_TYPE_IMAGE_MASK);
     } else {
         psFree(photRO->mask);
         photRO->mask = NULL;
     }
+    psMetadataAddPtr(photRO->parent->parent->analysis, PS_LIST_TAIL, "PSPHOT.PSF",
+                     PS_META_REPLACE | PS_DATA_UNKNOWN, "Point-spread function", data->psf);
+
+    psFree(photRO->analysis);
+    photRO->analysis = psMetadataAlloc();
 
     if (!psphotReadoutMinimal(config, view)) {
@@ -87,17 +94,23 @@
         psErrorStackPrint(stderr, "Unable to perform photometry on image");
         psWarning("Unable to perform photometry on image --- suspect bad data quality.");
-        ppSubDataQuality(config, data, psErrorCodeLast(), PPSUB_FILES_PHOT);
+        ppSubDataQuality(config, data, psErrorCodeLast(), PPSUB_FILES_PHOT_SUB | PPSUB_FILES_PHOT_INV);
     }
 
-    photRO->data_exists = true;
-    photRO->parent->data_exists = true;
-    photRO->parent->parent->data_exists = true;
+    if (!data->quality && !psMetadataCopySingle(inRO->analysis, photRO->analysis, "PSPHOT.SOURCES")) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to copy PSPHOT.SOURCES");
+        return false;
+    }
 
     if (data->stats) {
         psArray *sources = psMetadataLookupPtr(NULL, photRO->analysis, "PSPHOT.SOURCES"); // Sources
-        psMetadataAddS32(data->stats, PS_LIST_TAIL, "NUM_SOURCES", 0, "Number of sources detected",
-                         sources ? sources->n : 0);
-        psMetadataAddF32(data->stats, PS_LIST_TAIL, "TIME_PHOT", 0, "Time to do photometry",
-                         psTimerClear("PPSUB_PHOT"));
+        bool mdok;
+        int numSources = psMetadataLookupS32(&mdok, data->stats, "NUM_SOURCES"); // Number of sources
+        numSources += sources ? sources->n : 0;
+        psMetadataAddS32(data->stats, PS_LIST_TAIL, "NUM_SOURCES", PS_META_REPLACE,
+                         "Total number of sources detected", numSources);
+        float newTime = psTimerClear("PPSUB_PHOT"); // Time for photometry
+        float oldTime = psMetadataLookupF32(&mdok, data->stats, "TIME_PHOT"); // Previous time for photometry
+        psMetadataAddF32(data->stats, PS_LIST_TAIL, "TIME_PHOT", PS_META_REPLACE, "Time to do photometry",
+                         isfinite(oldTime) ? oldTime + newTime : newTime);
     }
 
Index: branches/pap/ppSub/src/ppSubReadoutStats.c
===================================================================
--- branches/pap/ppSub/src/ppSubReadoutStats.c	(revision 23711)
+++ branches/pap/ppSub/src/ppSubReadoutStats.c	(revision 23719)
@@ -1,8 +1,17 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 
-bool ppSubReadoutStats(pmConfig *config, ppSubData *data, const pmFPAview *view)
+#include <pslib.h>
+#include <psmodules.h>
+#include <ppStats.h>
+
+#include "ppSub.h"
+
+
+bool ppSubReadoutStats(pmConfig *config, ppSubData *data)
 {
     psAssert(config, "Require configuration");
     psAssert(data, "Require data");
-    psAssert(view, "Require view");
 
     if (!data->statsFile) {
@@ -14,8 +23,12 @@
     if (!output) {
         psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find file PPSUB.OUTPUT.\n");
-        goto ERROR;
+        return false;
     }
     psImageMaskType maskValue = pmConfigMaskGet("MASK.VALUE", config);
+    pmFPAview *view = ppSubViewReadout(); // View to readout
     ppStatsFPA(data->stats, output->fpa, view, maskValue, config);
+
+    pmReadout *outRO = pmFPAviewThisReadout(view, output->fpa); // Readout of interest
+    psFree(view);
 
     // Statistics on the matching
Index: branches/pap/ppSub/src/ppSubReadoutSubtract.c
===================================================================
--- branches/pap/ppSub/src/ppSubReadoutSubtract.c	(revision 23711)
+++ branches/pap/ppSub/src/ppSubReadoutSubtract.c	(revision 23719)
@@ -23,9 +23,7 @@
 #define WCS_TOLERANCE 0.001             // Tolerance for WCS
 
-bool ppSubReadoutSubtract(pmConfig *config, const pmFPAview *view)
+bool ppSubReadoutSubtract(pmConfig *config)
 {
     psAssert(config, "Require configuration");
-    psAssert(view, "Require view");
-
 
     // Look up recipe values
@@ -35,4 +33,6 @@
 
     bool reverse = psMetadataLookupBool(&mdok, config->arguments, "REVERSE"); // Reverse sense of subtraction?
+
+    pmFPAview *view = ppSubViewReadout(); // View to readout
 
     // Subtraction is: minuend - subtrahend
@@ -91,4 +91,5 @@
         psError(PS_ERR_UNKNOWN, false, "Unable to copy concepts from input to output.");
         psFree(outRO);
+        psFree(view);
         return false;
     }
@@ -99,4 +100,5 @@
     pmHDU *outHDU = outFPA->hdu;        // Output HDU
     pmChip *outChip = pmFPAfileThisChip(config->files, view, "PPSUB.OUTPUT"); // Output chip
+    psFree(view);
     if (!outHDU || !inHDU) {
         psError(PS_ERR_UNKNOWN, false, "Unable to find HDU at FPA level to copy astrometry.");
Index: branches/pap/ppSub/src/ppSubReadoutUpdate.c
===================================================================
--- branches/pap/ppSub/src/ppSubReadoutUpdate.c	(revision 23711)
+++ branches/pap/ppSub/src/ppSubReadoutUpdate.c	(revision 23719)
@@ -20,44 +20,2 @@
 
 #include "ppSub.h"
-
-bool ppSubReadoutUpdate(pmConfig *config, ppSubData *data, const pmFPAview *view)
-{
-    psAssert(config, "Require configuration");
-    psAssert(view, "Require view");
-
-    bool mdok = false;                  // Status of MD lookup
-
-    // Look up recipe values
-    psMetadata *recipe = psMetadataLookupMetadata(&mdok, config->recipes, PPSUB_RECIPE); // Recipe for ppSim
-    psAssert(recipe, "We checked this earlier, so it should be here.");
-
-    // Generate binned JPEGs
-    {
-        psImageMaskType maskBad = pmConfigMaskGet("BLANK", config); // Bits to mask for bad pixels
-
-        int bin1 = psMetadataLookupS32(NULL, recipe, "BIN1"); // First binning level
-        int bin2 = psMetadataLookupS32(NULL, recipe, "BIN2"); // Second binning level
-
-        // Target cells
-        pmCell *cell1 = pmFPAfileThisCell(config->files, view, "PPSUB.OUTPUT.JPEG1"); // Rebinned cell once
-        pmCell *cell2 = pmFPAfileThisCell(config->files, view, "PPSUB.OUTPUT.JPEG2"); // Rebinned cell twice
-
-        pmReadout *ro1 = pmReadoutAlloc(cell1), *ro2 = pmReadoutAlloc(cell2); // Binned readouts
-        if (!pmReadoutRebin(ro1, outRO, maskBad, bin1, bin1)) {
-            psError(PS_ERR_UNKNOWN, false, "Unable to bin output (1st binning)");
-            psFree(ro1);
-            psFree(ro2);
-            return false;
-        }
-        if (!pmReadoutRebin(ro2, ro1, 0, bin2, bin2)) {
-            psError(PS_ERR_UNKNOWN, false, "Unable to bin output (2nd binning)");
-            psFree(ro1);
-            psFree(ro2);
-            return false;
-        }
-        psFree(ro1);
-        psFree(ro2);
-    }
-
-    return true;
-}
Index: branches/pap/ppSub/src/ppSubSetMasks.c
===================================================================
--- branches/pap/ppSub/src/ppSubSetMasks.c	(revision 23711)
+++ branches/pap/ppSub/src/ppSubSetMasks.c	(revision 23719)
@@ -22,8 +22,7 @@
 #include "ppSub.h"
 
-bool ppSubSetMasks(pmConfig *config, const pmFPAview *view)
+bool ppSubSetMasks(pmConfig *config)
 {
     psAssert(config, "Require configuration");
-    psAssert(view, "Require view");
 
     psImageMaskType maskValue, markValue; // Mask values
@@ -50,5 +49,6 @@
     psAssert(lowValue, "LOW or BAD must be non-zero");
 
-    // input images
+    // Input images
+    pmFPAview *view = ppSubViewReadout(); // View to readout
     pmReadout *inRO = pmFPAfileThisReadout(config->files, view, "PPSUB.INPUT"); // Input readout
     pmReadout *refRO = pmFPAfileThisReadout(config->files, view, "PPSUB.REF"); // Reference readout
