Index: branches/eam_branches/20100225/ppSub/src/Makefile.am
===================================================================
--- branches/eam_branches/20100225/ppSub/src/Makefile.am	(revision 27087)
+++ branches/eam_branches/20100225/ppSub/src/Makefile.am	(revision 27517)
@@ -1,3 +1,3 @@
-bin_PROGRAMS = ppSub ppSubKernel
+bin_PROGRAMS = ppSub ppSubKernel ppSubConvolve
 
 if HAVE_SVNVERSION
@@ -34,4 +34,5 @@
 	ppSubData.c			\
 	ppSubErrorCodes.c		\
+	ppSubExit.c			\
 	ppSubFiles.c			\
 	ppSubLoop.c			\
@@ -53,4 +54,12 @@
 ppSubKernel_SOURCES =		\
 	ppSubKernel.c
+
+ppSubConvolve_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PPSTATS_CFLAGS) $(PSPHOT_CFLAGS) $(PPSUB_CFLAGS)
+ppSubConvolve_LDFLAGS  = $(PSLIB_LIBS)   $(PSMODULE_LIBS)   $(PPSTATS_LIBS)   $(PSPHOT_LIBS)   $(PPSUB_LIBS)
+
+ppSubConvolve_SOURCES =		\
+	ppSubConvolve.c		\
+	ppSubExit.c		\
+	ppSubVersion.c
 
 noinst_HEADERS = \
Index: branches/eam_branches/20100225/ppSub/src/ppSub.c
===================================================================
--- branches/eam_branches/20100225/ppSub/src/ppSub.c	(revision 27087)
+++ branches/eam_branches/20100225/ppSub/src/ppSub.c	(revision 27517)
@@ -26,4 +26,8 @@
     psLibInit(NULL);
     psTimerStart("ppSub");
+
+    pmErrorRegister();
+    ppSubErrorRegister();
+    psphotErrorRegister();
 
     ppSubData *data = NULL;             // Processing data
@@ -63,81 +67,50 @@
 
  die:
-    if (data && data->stats && data->statsFile) {
-        psString stats = psMetadataConfigFormat(data->stats); // Statistics to output
-        if (!stats || strlen(stats) == 0) {
-            psError(PPSUB_ERR_IO, false, "Unable to format statistics file");
-        } else if (fprintf(data->statsFile, "%s", stats) != strlen(stats)) {
-            psError(PPSUB_ERR_IO, true, "Unable to write statistics file");
+    {
+        psExit exitValue = ppSubExitCode(PS_EXIT_SUCCESS); // Exit code
+
+        if (data && data->stats && data->statsFile) {
+            psString stats = psMetadataConfigFormat(data->stats); // Statistics to output
+            if (!stats || strlen(stats) == 0) {
+                psError(PPSUB_ERR_IO, false, "Unable to format statistics file");
+            } else if (fprintf(data->statsFile, "%s", stats) != strlen(stats)) {
+                psError(PPSUB_ERR_IO, true, "Unable to write statistics file");
+            }
+            psFree(stats);
+            if (fclose(data->statsFile) == EOF) {
+                psError(PPSUB_ERR_IO, true, "Unable to close statistics file");
+            }
+            data->statsFile = NULL;
+            pmConfigRunFilenameAddWrite(data->config, "STATS", data->statsName);
+            exitValue = ppSubExitCode(exitValue);
         }
-        psFree(stats);
-        if (fclose(data->statsFile) == EOF) {
-            psError(PPSUB_ERR_IO, true, "Unable to close statistics file");
+
+        if (config && !ppSubFilesIterateUp(config, PPSUB_FILES_ALL)) {
+            psError(psErrorCodeLast(), false, "Unable to close files.");
+            exitValue = ppSubExitCode(exitValue);
+            pmFPAfileFreeSetStrict(false);
         }
-        data->statsFile = NULL;
-        pmConfigRunFilenameAddWrite(data->config, "STATS", data->statsName);
+
+        if (data) {
+            psString dump_file = psMetadataLookupStr(NULL, data->config->arguments, "-dumpconfig");
+            if (dump_file) {
+                if (!pmConfigDump(data->config, dump_file)) {
+                    psError(PPSUB_ERR_IO, false, "Unable to dump configuration.");
+                    exitValue = ppSubExitCode(exitValue);
+                }
+            }
+            psFree(data);
+        }
+
+        psTrace("ppSub", 1, "Finished at %f sec\n", psTimerMark("ppSub"));
+        psTimerStop();
+
+        pmVisualClose(); //close plot windows, if -visual is set
+        pmModelClassCleanup();
+        pmConfigDone();
+        psLibFinalize();
+
+        exitValue = ppSubExitCode(exitValue);
+        exit(exitValue);
     }
-
-    if (data) {
-        psString dump_file = psMetadataLookupStr(NULL, data->config->arguments, "-dumpconfig");
-        if (dump_file) {
-            if (!pmConfigDump(data->config, dump_file)) {
-                psError(PPSUB_ERR_IO, false, "Unable to dump configuration.");
-            }
-        }
-        psFree(data);
-    }
-
-    psExit exitValue = PS_EXIT_SUCCESS;        // Exit value for program
-    psErrorCode errorCode = psErrorCodeLast(); // Error code
-    if (errorCode != PS_ERR_NONE) {
-        psErrorStackPrint(stderr, "Unable to perform subtraction.");
-        switch (errorCode) {
-          case PPSUB_ERR_UNKNOWN:
-          case PS_ERR_UNKNOWN:
-            exitValue = PS_EXIT_UNKNOWN_ERROR;
-            break;
-          case PS_ERR_IO:
-          case PS_ERR_DB_CLIENT:
-          case PS_ERR_DB_SERVER:
-          case PS_ERR_BAD_FITS:
-          case PS_ERR_OS_CALL_FAILED:
-          case PPSUB_ERR_IO:
-            exitValue = PS_EXIT_SYS_ERROR;
-            break;
-          case PS_ERR_BAD_PARAMETER_VALUE:
-          case PS_ERR_BAD_PARAMETER_TYPE:
-          case PS_ERR_BAD_PARAMETER_NULL:
-          case PS_ERR_BAD_PARAMETER_SIZE:
-          case PPSUB_ERR_ARGUMENTS:
-          case PPSUB_ERR_CONFIG:
-            exitValue = PS_EXIT_CONFIG_ERROR;
-            break;
-          case PPSUB_ERR_DATA:
-          case PPSUB_ERR_NO_OVERLAP:
-            exitValue = PS_EXIT_DATA_ERROR;
-            break;
-          case PS_ERR_UNEXPECTED_NULL:
-          case PS_ERR_PROGRAMMING:
-          case PPSUB_ERR_NOT_IMPLEMENTED:
-          case PPSUB_ERR_PROG:
-          default:
-            // It's a programming error if we're not dealing with the error correctly
-            exitValue = PS_EXIT_PROG_ERROR;
-            break;
-        }
-    }
-
-    if (config && !ppSubFilesIterateUp(config, PPSUB_FILES_ALL)) {
-        psErrorStackPrint(stderr, "Unable to close files.");
-    }
-
-    psTrace("ppSub", 1, "Finished at %f sec\n", psTimerMark("ppSub"));
-    psTimerStop();
-
-    pmVisualClose(); //close plot windows, if -visual is set
-    pmModelClassCleanup();
-    pmConfigDone();
-    psLibFinalize();
-
-    exit(exitValue);
 }
Index: branches/eam_branches/20100225/ppSub/src/ppSub.h
===================================================================
--- branches/eam_branches/20100225/ppSub/src/ppSub.h	(revision 27087)
+++ branches/eam_branches/20100225/ppSub/src/ppSub.h	(revision 27517)
@@ -165,4 +165,8 @@
 bool ppSubCopyPSF (pmFPAfile *output, pmFPAfile *input, pmFPAview *view);
 
+/// Return appropriate exit code
+psExit ppSubExitCode(psExit exitValue   // Current exit value
+    );
+
 ///@}
 #endif
Index: branches/eam_branches/20100225/ppSub/src/ppSubArguments.c
===================================================================
--- branches/eam_branches/20100225/ppSub/src/ppSubArguments.c	(revision 27087)
+++ branches/eam_branches/20100225/ppSub/src/ppSubArguments.c	(revision 27517)
@@ -141,4 +141,8 @@
     if (data->statsName && strlen(data->statsName) > 0) {
         psString resolved = pmConfigConvertFilename(data->statsName, config, true, true); // Resolved filename
+        if (!resolved) {
+            psError(psErrorCodeLast(), false, "Unable to resolve statistics file: %s", data->statsName);
+            return false;
+        }
         data->statsFile = fopen(resolved, "w");
         if (!data->statsFile) {
Index: branches/eam_branches/20100225/ppSub/src/ppSubConvolve.c
===================================================================
--- branches/eam_branches/20100225/ppSub/src/ppSubConvolve.c	(revision 27517)
+++ branches/eam_branches/20100225/ppSub/src/ppSubConvolve.c	(revision 27517)
@@ -0,0 +1,285 @@
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppSub.h"
+
+// Print usage information and die
+static void usage(const char *program,  // Name of the program
+                  psMetadata *arguments, // Command-line arguments
+                  pmConfig *config      // Configuration
+    )
+{
+    fprintf(stderr, "\nPan-STARRS image convolution\n\n");
+    fprintf(stderr, "Usage: %s OUTPUT_ROOT\n\n",
+            program);
+    fprintf(stderr, "\n");
+    psArgumentHelp(arguments);
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+    exit(PS_EXIT_CONFIG_ERROR);
+}
+
+// Put filename in it's own list
+static void fileList(const char *file, // The symbolic name for the file
+                     const char *name, // The name of the file
+                     const char *comment, // Description of the file
+                     pmConfig *config // Configuration
+    )
+{
+    psArray *files = psArrayAlloc(1); // Array with file names
+    files->data[0] = psStringCopy(name);
+    psMetadataAddArray(config->arguments, PS_LIST_TAIL, file, 0, comment, files);
+    psFree(files);
+    return;
+}
+
+
+int main(int argc, char *argv[])
+{
+    pmFPAview *view = pmFPAviewAlloc(0); // Pointer into FPA hierarchy
+
+    pmConfig *config = pmConfigRead(&argc, argv, PPSUB_RECIPE); // Configuration
+    if (!config) {
+        psError(psErrorCodeLast(), false, "Unable to read configuration");
+        goto die;
+    }
+
+    bool reference = false;             // Input is actually the reference image?
+    int threads = 0;                    // Number of threads
+    {
+        psMetadata *arguments = config->arguments; // Command-line arguments
+        psMetadataAddStr(arguments, PS_LIST_TAIL, "-image", 0, "Input image", NULL);
+        psMetadataAddStr(arguments, PS_LIST_TAIL, "-mask", 0, "Input mask", NULL);
+        psMetadataAddStr(arguments, PS_LIST_TAIL, "-kernel", 0, "Convolution kernel", NULL);
+        psMetadataAddBool(arguments, PS_LIST_TAIL, "-reference", 0, "Input is actually reference?", false);
+        psMetadataAddS32(arguments, PS_LIST_TAIL, "-threads", 0, "Threads to use", 0);
+
+        if (argc == 1 || !psArgumentParse(arguments, &argc, argv) || argc != 2) {
+            usage(argv[0], arguments, config);
+        }
+
+        const char *inImage = psMetadataLookupStr(NULL, arguments, "-image"); // Input image
+        const char *inMask = psMetadataLookupStr(NULL, arguments, "-mask"); // Input mask
+        const char *inKernel = psMetadataLookupStr(NULL, arguments, "-kernel"); // Input kernel
+        if (!inImage || !inKernel) {
+            usage(argv[0], arguments, config);
+        }
+
+        reference = psMetadataLookupBool(NULL, arguments, "-reference");
+        threads = psMetadataLookupS32(NULL, arguments, "-threads");
+        fileList("PPSUB.INPUT", inImage, "Input image", config);
+        fileList("PPSUB.INPUT.MASK", inMask, "Input mask", config);
+        fileList("PPSUB.INPUT.KERNEL", inKernel, "Input kernel", config);
+        psMetadataAddStr(config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "Name of the output image", argv[1]);
+
+        bool status;                    // Status of file definition
+        pmFPAfile *image = pmFPAfileDefineFromArgs(&status, config, "PPSUB.INPUT", "PPSUB.INPUT");
+        if (!image || !status || image->type != PM_FPA_FILE_IMAGE) {
+            psError(PPSUB_ERR_CONFIG, false, "Unable to define input image file.");
+            goto die;
+        }
+        pmFPAfile *mask = pmFPAfileBindFromArgs(&status, image, config, "PPSUB.INPUT.MASK",
+                                                "PPSUB.INPUT.MASK");
+        if (!mask || !status || mask->type != PM_FPA_FILE_MASK) {
+            psError(PPSUB_ERR_CONFIG, false, "Unable to define input mask file.");
+            goto die;
+        }
+
+        pmFPAfile *kernel = pmFPAfileBindFromArgs(&status, image, config, "PPSUB.INPUT.KERNEL",
+                                                  "PPSUB.INPUT.KERNEL");
+        if (!kernel || !status || kernel->type != PM_FPA_FILE_SUBKERNEL) {
+            psError(PPSUB_ERR_CONFIG, false, "Unable to define kernel file.");
+            goto die;
+        }
+
+        pmFPAfile *output = pmFPAfileDefineFromFile(config, image, 1, 1, "PPSUB.INPUT.CONV");
+        if (!output || output->type != PM_FPA_FILE_IMAGE) {
+            psError(PPSUB_ERR_CONFIG, false, "Unable to define output file.");
+            goto die;
+        }
+        output->save = true;
+    }
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSUB_RECIPE); // Recipe for ppSim
+    if (!recipe) {
+        psError(PPSUB_ERR_CONFIG, false, "Unable to find recipe %s", PPSUB_RECIPE);
+        goto die;
+    }
+
+    // Read everything
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+        psError(psErrorCodeLast(), false, "File checks failed.");
+        goto die;
+    }
+    view->chip = 0;
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+        psError(psErrorCodeLast(), false, "File checks failed.");
+        goto die;
+    }
+    view->cell = 0;
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+        psError(psErrorCodeLast(), false, "File checks failed.");
+        goto die;
+    }
+    view->readout = 0;
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+        psError(psErrorCodeLast(), false, "File checks failed.");
+        goto die;
+    }
+
+
+    pmReadout *inRO = pmFPAfileThisReadout(config->files, view, "PPSUB.INPUT"); // Input readout
+    pmSubtractionKernels *kernels = psMetadataLookupPtr(NULL, inRO->analysis,
+                                                        PM_SUBTRACTION_ANALYSIS_KERNEL); // Convolution kernel
+    pmCell *outCell = pmFPAfileThisCell(config->files, view, "PPSUB.INPUT.CONV"); // Output cell
+    pmReadout *outRO = pmReadoutAlloc(outCell);                                   // Output readout
+    pmConceptsCopyFPA(outRO->parent->parent->parent, inRO->parent->parent->parent, true, true);
+    pmHDU *inHDU = pmHDUFromCell(inRO->parent); // Input header
+    pmHDU *outHDU = pmHDUFromCell(outCell);     // Output header
+    outHDU->header = psMetadataCopy(outHDU->header, inHDU->header);
+    ppSubVersionHeader(outHDU->header);
+
+    pmReadout *input, *ref, *inConv, *refConv; // Input and output readouts
+    if (reference) {
+        ref = inRO;
+        refConv = outRO;
+        input = NULL;
+        inConv = NULL;
+    } else {
+        input = inRO;
+        inConv = outRO;
+        ref = NULL;
+        refConv = NULL;
+    }
+
+    bool convolve = false;              // Do we need to convolve?
+    float norm = 1.0;                   // Normalisation to apply
+    switch (kernels->mode) {
+      case PM_SUBTRACTION_MODE_1:
+        if (!reference) {
+            convolve = true;
+        }
+        norm = 1.0 / kernels->solution1->data.F64[PM_SUBTRACTION_INDEX_NORM(kernels)];
+        break;
+      case PM_SUBTRACTION_MODE_2:
+        if (reference) {
+            convolve = true;
+        }
+        // No normalisation; should already be normalised appropriately
+        break;
+      case PM_SUBTRACTION_MODE_DUAL: {
+          // We only have a single input, so hack together a single-mode kernel
+          int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index of normalisation term
+          norm = 1.0 / kernels->solution1->data.F64[normIndex];
+          convolve = true;
+          if (reference) {
+              for (int i = 0; i < kernels->solution2->n; i++) {
+                  kernels->solution1->data.F64[i] = -kernels->solution2->data.F64[i];
+              }
+              kernels->solution1->data.F64[normIndex] = 1.0;
+              kernels->solution1->data.F64[PM_SUBTRACTION_INDEX_BG(kernels)] = 0.0;
+              kernels->mode = PM_SUBTRACTION_MODE_2;
+          } else {
+              kernels->mode = PM_SUBTRACTION_MODE_1;
+          }
+          psFree(kernels->solution2);
+          kernels->solution2 = NULL;
+          break;
+      }
+      default:
+        psAbort("Unrecognised kernel mode: %x", kernels->mode);
+    }
+
+    if (convolve) {
+        int stride = psMetadataLookupS32(NULL, recipe, "STRIDE"); // Size of convolution patches
+        float kernelErr = psMetadataLookupF32(NULL, recipe, "KERNEL.ERR"); // Relative sys error in kernel
+        float covarFrac = psMetadataLookupF32(NULL, recipe, "COVAR.FRAC"); // Fraction for covar calculation
+        float badFrac = psMetadataLookupF32(NULL, recipe, "BADFRAC"); // Maximum bad fraction
+        float poorFrac = psMetadataLookupF32(NULL, recipe, "POOR.FRACTION"); // Fraction for "poor"
+
+        psImageMaskType maskVal = pmConfigMaskGet("MASK.VALUE", config); // Bits to mask in inputs
+        psImageMaskType maskPoor = pmConfigMaskGet("CONV.POOR", config); // Bits to mask for poor pixels
+        psImageMaskType maskBad = pmConfigMaskGet("BLANK", config); // Bits to mask for bad pixels
+
+        if (threads > 0) {
+            pmSubtractionThreadsInit();
+        }
+        if (!pmSubtractionMatchPrecalc(inConv, refConv, input, ref, inRO->analysis,
+                                       stride, kernelErr, covarFrac, maskVal, maskBad, maskPoor,
+                                       poorFrac, badFrac)) {
+            psError(psErrorCodeLast(), false, "Unable to convolve images.");
+            if (threads > 0) {
+                pmSubtractionThreadsFinalize();
+            }
+            goto die;
+        }
+        if (threads > 0) {
+            pmSubtractionThreadsFinalize();
+        }
+    } else {
+        // Subtract background, since that's what ppSub will do with it
+        psImageMaskType maskVal = pmConfigMaskGet("MASK.VALUE", config); // Bits to mask in inputs
+        psStats *bg = psStatsAlloc(PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV); // Statistics for background
+        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS);
+        psStatsInit(bg);
+        if (!psImageBackground(bg, NULL, inRO->image, inRO->mask, maskVal, rng)) {
+            psError(PPSUB_ERR_DATA, false, "Unable to measure background statistics.");
+            psFree(bg);
+            psFree(rng);
+            goto die;
+        }
+        psFree(rng);
+        outRO->image = (psImage*)psBinaryOp(outRO->image, inRO->image, "-",
+                                            psScalarAlloc((float)bg->robustMedian, PS_TYPE_F32));
+        psFree(bg);
+        outRO->data_exists = outRO->parent->data_exists = outRO->parent->parent->data_exists = true;
+    }
+
+    if (norm != 1.0) {
+        psBinaryOp(outRO->image, outRO->image, "*", psScalarAlloc(norm, PS_TYPE_F32));
+    }
+
+    psFree(outRO);
+
+ die:
+    {
+        psExit exitValue = ppSubExitCode(PS_EXIT_SUCCESS); // Exit value
+
+        // Write everything
+        view->chip = view->cell = view->readout = 0;
+        if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+            psError(psErrorCodeLast(), false, "File checks failed.");
+            exitValue = ppSubExitCode(exitValue);
+            pmFPAfileFreeSetStrict(false);
+        }
+        view->readout = -1;
+        if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+            psError(psErrorCodeLast(), false, "File checks failed.");
+            exitValue = ppSubExitCode(exitValue);
+            pmFPAfileFreeSetStrict(false);
+        }
+        view->cell = -1;
+        if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+            psError(psErrorCodeLast(), false, "File checks failed.");
+            exitValue = ppSubExitCode(exitValue);
+            pmFPAfileFreeSetStrict(false);
+        }
+        view->chip = -1;
+        if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+            psError(psErrorCodeLast(), false, "File checks failed.");
+            exitValue = ppSubExitCode(exitValue);
+            pmFPAfileFreeSetStrict(false);
+        }
+        psFree(view);
+
+        psFree(config);
+        pmConceptsDone();
+        pmConfigDone();
+        psLibFinalize();
+        exit(exitValue);
+    }
+
+    psAbort("Should never reach here.");
+}
Index: branches/eam_branches/20100225/ppSub/src/ppSubExit.c
===================================================================
--- branches/eam_branches/20100225/ppSub/src/ppSubExit.c	(revision 27517)
+++ branches/eam_branches/20100225/ppSub/src/ppSubExit.c	(revision 27517)
@@ -0,0 +1,63 @@
+#include <stdio.h>
+#include <pslib.h>
+#include <psphotErrorCodes.h>
+
+#include "ppSub.h"
+
+psExit ppSubExitCode(psExit exitValue)
+{
+    if (exitValue != PS_EXIT_SUCCESS) {
+        return exitValue;
+    }
+
+    psErrorCode errorCode = psErrorCodeLast(); // Error code
+    if (errorCode != PS_ERR_NONE) {
+        psErrorStackPrint(stderr, "Error in subtraction:");
+        switch (errorCode) {
+          case PPSUB_ERR_UNKNOWN:
+          case PS_ERR_UNKNOWN:
+            psLogMsg("ppStack", PS_LOG_WARN, "Unknown error code: %x", errorCode);
+            exitValue = PS_EXIT_UNKNOWN_ERROR;
+            break;
+          case PS_ERR_IO:
+          case PS_ERR_DB_CLIENT:
+          case PS_ERR_DB_SERVER:
+          case PS_ERR_BAD_FITS:
+          case PS_ERR_OS_CALL_FAILED:
+          case PM_ERR_SYS:
+          case PPSUB_ERR_IO:
+            psLogMsg("ppStack", PS_LOG_WARN, "I/O error code: %x", errorCode);
+            exitValue = PS_EXIT_SYS_ERROR;
+            break;
+          case PS_ERR_BAD_PARAMETER_VALUE:
+          case PS_ERR_BAD_PARAMETER_TYPE:
+          case PS_ERR_BAD_PARAMETER_NULL:
+          case PS_ERR_BAD_PARAMETER_SIZE:
+          case PPSUB_ERR_ARGUMENTS:
+          case PPSUB_ERR_CONFIG:
+            psLogMsg("ppStack", PS_LOG_WARN, "Configuration error code: %x", errorCode);
+            exitValue = PS_EXIT_CONFIG_ERROR;
+            break;
+          case PSPHOT_ERR_PSF:
+          case PPSUB_ERR_DATA:
+          case PPSUB_ERR_NO_OVERLAP:
+            psLogMsg("ppStack", PS_LOG_WARN, "Data error code: %x", errorCode);
+            exitValue = PS_EXIT_DATA_ERROR;
+            break;
+          case PS_ERR_UNEXPECTED_NULL:
+          case PS_ERR_PROGRAMMING:
+          case PPSUB_ERR_NOT_IMPLEMENTED:
+          case PPSUB_ERR_PROG:
+            psLogMsg("ppStack", PS_LOG_WARN, "Programming error code: %x", errorCode);
+            exitValue = PS_EXIT_PROG_ERROR;
+            break;
+          default:
+            // It's a programming error if we're not dealing with the error correctly
+            psLogMsg("ppStack", PS_LOG_WARN, "Unrecognised error code: %x", errorCode);
+            exitValue = PS_EXIT_PROG_ERROR;
+            break;
+        }
+    }
+
+    return exitValue;
+}
Index: branches/eam_branches/20100225/ppSub/src/ppSubFiles.c
===================================================================
--- branches/eam_branches/20100225/ppSub/src/ppSubFiles.c	(revision 27087)
+++ branches/eam_branches/20100225/ppSub/src/ppSubFiles.c	(revision 27517)
@@ -183,4 +183,41 @@
     }
 
+    psFree(view);
+
+    // Disable writing of data now that we've written things out
+    if (files & PPSUB_FILES_CONV) {
+        pmFPAview *view = ppSubViewReadout(); // View to readout
+        {
+            pmReadout *ro = pmFPAfileThisReadout(config->files, view, "PPSUB.INPUT.CONV");
+            if (ro) {
+                ro->data_exists = ro->parent->data_exists = ro->parent->parent->data_exists = false;
+            }
+        }
+        {
+            pmReadout *ro = pmFPAfileThisReadout(config->files, view, "PPSUB.REF.CONV");
+            if (ro) {
+                ro->data_exists = ro->parent->data_exists = ro->parent->parent->data_exists = false;
+            }
+        }
+        psFree(view);
+    }
+    if (files & PPSUB_FILES_SUB) {
+        pmFPAview *view = ppSubViewReadout(); // View to readout
+        pmReadout *ro = pmFPAfileThisReadout(config->files, view, "PPSUB.OUTPUT");
+        if (ro) {
+            ro->data_exists = ro->parent->data_exists = ro->parent->parent->data_exists = false;
+        }
+        psFree(view);
+    }
+    // Disable writing of data now that we've written things out
+    if (files & PPSUB_FILES_INV) {
+        pmFPAview *view = ppSubViewReadout(); // View to readout
+        pmReadout *ro = pmFPAfileThisReadout(config->files, view, "PPSUB.INVERSE");
+        if (ro) {
+            ro->data_exists = ro->parent->data_exists = ro->parent->parent->data_exists = false;
+        }
+        psFree(view);
+    }
+
     ppSubFilesActivate(config, PPSUB_FILES_ALL, false);
 
Index: branches/eam_branches/20100225/ppSub/src/ppSubMatchPSFs.c
===================================================================
--- branches/eam_branches/20100225/ppSub/src/ppSubMatchPSFs.c	(revision 27087)
+++ branches/eam_branches/20100225/ppSub/src/ppSubMatchPSFs.c	(revision 27517)
@@ -331,5 +331,5 @@
     int threads = psMetadataLookupS32(NULL, config->arguments, "-threads"); // Number of threads
     if (threads > 0) {
-        pmSubtractionThreadsInit(inRO, refRO);
+        pmSubtractionThreadsInit();
     }
 
@@ -340,4 +340,8 @@
         psImageCovarianceTruncate(refRO->covariance, COVAR_FRAC);
     }
+
+    // Data doesn't exist until it's created in pmSubtraction...
+    inConv->data_exists = inConv->parent->data_exists = inConv->parent->parent->data_exists = false;
+    refConv->data_exists = refConv->parent->data_exists = refConv->parent->parent->data_exists = false;
 
     // Match the PSFs
@@ -349,9 +353,9 @@
     } else {
         success = pmSubtractionMatch(inConv, refConv, inRO, refRO, footprint, stride, regionSize,
-                                     spacing, threshold, detections->allSources, data->stamps, type, size, order,
-                                     widths, orders, inner, ringsOrder, binning, penalty, optimum,
-                                     optWidths, optOrder, optThresh, iter, rej, normFrac,
-                                     sysErr, skyErr, kernelErr, covarFrac, maskVal, maskBad, maskPoor,
-                                     poorFrac, badFrac, subMode);
+                                     spacing, threshold, detections ? detections->allSources : NULL,
+                                     data->stamps, type, size, order, widths, orders, inner, ringsOrder,
+                                     binning, penalty, optimum, optWidths, optOrder, optThresh, iter, rej,
+                                     normFrac, sysErr, skyErr, kernelErr, covarFrac,
+                                     maskVal, maskBad, maskPoor, poorFrac, badFrac, subMode);
     }
 
@@ -376,5 +380,5 @@
 
     psFree(optWidths);
-    pmSubtractionThreadsFinalize(inRO, refRO);
+    pmSubtractionThreadsFinalize();
 
     if (!success) {
Index: branches/eam_branches/20100225/ppSub/src/ppSubVarianceRescale.c
===================================================================
--- branches/eam_branches/20100225/ppSub/src/ppSubVarianceRescale.c	(revision 27087)
+++ branches/eam_branches/20100225/ppSub/src/ppSubVarianceRescale.c	(revision 27517)
@@ -54,4 +54,10 @@
     pmReadout *readout = pmFPAfileThisReadout(config->files, view, "PPSUB.OUTPUT"); // Output image
 
+    if (!readout->variance) {
+        // Nothing to renormalise
+        psWarning("Renormalisation of the variance requested, but no variance provided.");
+        return true;
+    }
+
     return pmReadoutVarianceRenormalise(readout, maskBad, num, minValid, maxValid);
 }
