Index: /trunk/ppStack/src/ppStack.c
===================================================================
--- /trunk/ppStack/src/ppStack.c	(revision 29551)
+++ /trunk/ppStack/src/ppStack.c	(revision 29552)
@@ -124,4 +124,5 @@
         psLibFinalize();
         pmVisualClose();
+	pmVisualCleanup ();
 
         exitValue = ppStackExitCode(exitValue);
Index: /trunk/ppStack/src/ppStackCombineFinal.c
===================================================================
--- /trunk/ppStack/src/ppStackCombineFinal.c	(revision 29551)
+++ /trunk/ppStack/src/ppStackCombineFinal.c	(revision 29552)
@@ -93,5 +93,13 @@
 
     // Sum covariance matrices
+    // the array may be defined, but no covariances actually supplied.
+    bool haveCovariances = false;
     if (covariances) {
+	for (int i = 0; i < covariances->n; i++) {
+	    haveCovariances |= (covariances->data[i] != NULL);
+	}
+    }
+
+    if (haveCovariances) {
         outRO->covariance = psImageCovarianceAverageWeighted(covariances, options->weightings);
     } else {
Index: /trunk/ppStack/src/ppStackLoop.c
===================================================================
--- /trunk/ppStack/src/ppStackLoop.c	(revision 29551)
+++ /trunk/ppStack/src/ppStackLoop.c	(revision 29552)
@@ -204,4 +204,5 @@
 #if 1
     // Unconvolved stack --- it's cheap to calculate, compared to everything else!
+    // XXX unconvolved stack is currently using the convolved mask!  oops!
     if (options->convolve) {
         // Start threading
Index: /trunk/ppStack/src/ppStackMatch.c
===================================================================
--- /trunk/ppStack/src/ppStackMatch.c	(revision 29551)
+++ /trunk/ppStack/src/ppStackMatch.c	(revision 29552)
@@ -68,5 +68,5 @@
 
     // user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
-    psMetadataAddU8(psphotRecipe, PS_LIST_TAIL, "MASK.PSPHOT", PS_META_REPLACE, "user-defined mask", maskBad);
+    psMetadataAddImageMask(psphotRecipe, PS_LIST_TAIL, "MASK.PSPHOT", PS_META_REPLACE, "user-defined mask", maskBad);
 
     psImage *binned = psphotModelBackgroundReadoutNoFile(ro, config); // Binned background model
@@ -153,5 +153,6 @@
     int threads = psMetadataLookupS32(NULL, config->arguments, "-threads"); // Number of threads
 
-    if (!pmReadoutMaskNonfinite(readout, maskVal)) {
+    // Replaced pmReadoutMaskNonfinite with pmReadoutMaskInvalid (tests for already masked pixels)
+    if (!pmReadoutMaskInvalid(readout, maskVal, maskBad)) {
         psError(psErrorCodeLast(), false, "Unable to mask non-finite pixels in readout.");
         return false;
@@ -319,9 +320,10 @@
                 }
             } else {
+		// we need to register the FWHM values for use downstream 
+		pmSubtractionSetFWHMs(options->inputSeeing->data.F32[index], options->targetSeeing);
+
                 // Scale the input parameters
                 psVector *widthsCopy = psVectorCopy(NULL, widths, PS_TYPE_F32); // Copy of kernel widths
-                if (scale && !pmSubtractionParamsScale(&size, &footprint, widthsCopy,
-                                                       options->inputSeeing->data.F32[index],
-                                                       options->targetSeeing, scaleRef, scaleMin, scaleMax)) {
+                if (scale && !pmSubtractionParamsScale(&size, &footprint, widthsCopy, scaleRef, scaleMin, scaleMax)) {
                     psError(psErrorCodeLast(), false, "Unable to scale kernel parameters");
                     psFree(fake);
Index: /trunk/ppStack/src/ppStackPSF.c
===================================================================
--- /trunk/ppStack/src/ppStackPSF.c	(revision 29551)
+++ /trunk/ppStack/src/ppStackPSF.c	(revision 29552)
@@ -15,43 +15,60 @@
 {
     bool mdok = false;
+    pmPSF *psf = NULL;
 
-#ifndef TESTING
     // Get the recipe values
     psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
     psAssert(recipe, "We've thrown an error on this before.");
 
-    int psfInstances = psMetadataLookupS32(NULL, recipe, "PSF.INSTANCES"); // Number of instances for PSF
-    float psfRadius = psMetadataLookupF32(NULL, recipe, "PSF.RADIUS"); // Radius for PSF
-    const char *psfModel = psMetadataLookupStr(NULL, recipe, "PSF.MODEL"); // Model for PSF
-    int psfOrder = psMetadataLookupS32(NULL, recipe, "PSF.ORDER"); // Spatial order for PSF
-
-    psString maskValStr = psMetadataLookupStr(&mdok, recipe, "MASK.VAL"); // Name of bits to mask going in
-    if (!mdok || !maskValStr) {
-        psError(PPSTACK_ERR_CONFIG, false, "Unable to find MASK.VAL in recipe");
-        return false;
-    }
-    psImageMaskType maskVal = pmConfigMaskGet(maskValStr, config); // Bits to mask
-
-    for (int i = 0; i < psfs->n; i++) {
-        if (inputMask->data.U8[i]) {
-            psFree(psfs->data[i]);
-            psfs->data[i] = NULL;
-        }
+    bool autosize = psMetadataLookupBool(&mdok, recipe, "PSF.AUTOSIZE"); // Spatial order for PSF
+    if (!mdok) {
+	// older config files which lack PSF.AUTOSIZE used TRUE as the default
+	autosize = true;
     }
 
-    // Solve for the target PSF
-    pmPSF *psf = pmPSFEnvelope(numCols, numRows, psfs, psfInstances, psfRadius, psfModel, psfOrder, psfOrder, maskVal);
-    if (!psf) {
-        psError(PPSTACK_ERR_PSF, false, "Unable to determine output PSF.");
-        return NULL;
+    char *psfModel = psMetadataLookupStr(NULL, recipe, "PSF.MODEL"); // Model for PSF
+
+    if (autosize) {
+
+	int psfInstances = psMetadataLookupS32(NULL, recipe, "PSF.INSTANCES"); // Number of instances for PSF
+	float psfRadius = psMetadataLookupF32(NULL, recipe, "PSF.RADIUS"); // Radius for PSF
+	int psfOrder = psMetadataLookupS32(NULL, recipe, "PSF.ORDER"); // Spatial order for PSF
+
+	psString maskValStr = psMetadataLookupStr(&mdok, recipe, "MASK.VAL"); // Name of bits to mask going in
+	if (!mdok || !maskValStr) {
+	    psError(PPSTACK_ERR_CONFIG, false, "Unable to find MASK.VAL in recipe");
+	    return NULL;
+	}
+	psImageMaskType maskVal = pmConfigMaskGet(maskValStr, config); // Bits to mask
+
+	for (int i = 0; i < psfs->n; i++) {
+	    if (inputMask->data.U8[i]) {
+		psFree(psfs->data[i]);
+		psfs->data[i] = NULL;
+	    }
+	}
+
+	// Solve for the target PSF
+	psf = pmPSFEnvelope(numCols, numRows, psfs, psfInstances, psfRadius, psfModel, psfOrder, psfOrder, maskVal);
+	if (!psf) {
+	    psError(PPSTACK_ERR_PSF, false, "Unable to determine output PSF.");
+	    return NULL;
+	}
+    } else {
+
+	// Manually defined target PSF 
+	float psfFWHM = psMetadataLookupF32(&mdok, recipe, "PSF.OUTPUT.FWHM"); // Radius for PSF
+	if (!mdok) {
+	    psfFWHM = 4.0;
+	}
+
+	float Mxx = M_SQRT2 * psfFWHM / 2.35;
+
+	psf = pmPSFBuildSimple(psfModel, Mxx, Mxx, 0.0, 0.0);
+	if (!psf) {
+	    psError(PPSTACK_ERR_PSF, false, "Unable to build dummy PSF.");
+	    return NULL;
+	}
     }
-#else
-    // Dummy PSF
-    pmPSF *psf = pmPSFBuildSimple("PS_MODEL_PS1_V1", 4.0, 4.0, 0.0, 1.0);
-    if (!psf) {
-        psError(PPSTACK_ERR_PSF, false, "Unable to build dummy PSF.");
-        return NULL;
-    }
-#endif
 
     return psf;
Index: /trunk/ppStack/src/ppStackReadout.c
===================================================================
--- /trunk/ppStack/src/ppStackReadout.c	(revision 29551)
+++ /trunk/ppStack/src/ppStackReadout.c	(revision 29552)
@@ -130,10 +130,23 @@
     int kernelSize = psMetadataLookupS32(NULL, ppsub, "KERNEL.SIZE"); // Kernel half-size
 
-    psString maskValStr = psMetadataLookupStr(NULL, recipe, "MASK.VAL"); // Name of bits to mask going in
-    psImageMaskType maskVal = pmConfigMaskGet(maskValStr, config); // Bits to mask going in to pmSubtractionMatch
+    psString maskBadStr = psMetadataLookupStr(NULL, recipe, "MASK.VAL"); // Name of bits to mask for bad
+    psImageMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
+
     psString maskSuspectStr = psMetadataLookupStr(NULL, recipe, "MASK.SUSPECT"); // Name of suspect mask bits
     psImageMaskType maskSuspect = pmConfigMaskGet(maskSuspectStr, config); // Suspect bits
-    psString maskBadStr = psMetadataLookupStr(NULL, recipe, "MASK.BAD"); // Name of bits to mask for bad
-    psImageMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
+
+    bool status = false;
+    psImageMaskType maskBlank;
+    psString maskBlankStr = psMetadataLookupStr(&status, recipe, "MASK.BLANK"); // Name of bits to set for empty pixels
+    if (maskBlankStr) {
+      maskBlank = pmConfigMaskGet(maskBlankStr, config); // Bits to mask for bad pixels
+    } else {
+      maskBlankStr = psMetadataLookupStr(&status, recipe, "MASK.BAD"); // Old name for MASK.BLANK
+      if (maskBlankStr) {
+	maskBlank = pmConfigMaskGet(maskBlankStr, config); // Bits to mask for bad pixels
+      } else {
+	maskBlank = pmConfigMaskGet("BLANK", config); // Bits to mask for bad pixels
+      }
+    }
 
     int num = readouts->n;              // Number of inputs
@@ -157,5 +170,5 @@
     }
 
-    if (!pmStackCombine(outRO, NULL, stack, maskVal | maskBad, maskSuspect, maskBad, kernelSize, iter,
+    if (!pmStackCombine(outRO, NULL, stack, maskBad, maskSuspect, maskBlank, kernelSize, iter,
                         combineRej, combineSys, combineDiscard, useVariance, safe, false)) {
         psError(PS_ERR_UNKNOWN, false, "Unable to combine input readouts with rejection.");
@@ -215,10 +228,23 @@
     bool safe = psMetadataLookupBool(&mdok, recipe, "SAFE"); // Be safe when combining small numbers of pixels
 
-    psString maskValStr = psMetadataLookupStr(NULL, recipe, "MASK.VAL"); // Name of bits to mask going in
-    psImageMaskType maskVal = pmConfigMaskGet(maskValStr, config); // Bits to mask going in to pmSubtractionMatch
+    psString maskBadStr = psMetadataLookupStr(NULL, recipe, "MASK.VAL"); // Name of bits to mask for bad
+    psImageMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
+
     psString maskSuspectStr = psMetadataLookupStr(NULL, recipe, "MASK.SUSPECT"); // Name of suspect mask bits
     psImageMaskType maskSuspect = pmConfigMaskGet(maskSuspectStr, config); // Suspect bits
-    psString maskBadStr = psMetadataLookupStr(NULL, recipe, "MASK.BAD"); // Name of bits to mask for bad
-    psImageMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
+
+    bool status = false;
+    psImageMaskType maskBlank;
+    psString maskBlankStr = psMetadataLookupStr(&status, recipe, "MASK.BLANK"); // Name of bits to set for empty pixels
+    if (maskBlankStr) {
+      maskBlank = pmConfigMaskGet(maskBlankStr, config); // Bits to mask for bad pixels
+    } else {
+      maskBlankStr = psMetadataLookupStr(&status, recipe, "MASK.BAD"); // Old name for MASK.BLANK
+      if (maskBlankStr) {
+	maskBlank = pmConfigMaskGet(maskBlankStr, config); // Bits to mask for bad pixels
+      } else {
+	maskBlank = pmConfigMaskGet("BLANK", config); // Bits to mask for bad pixels
+      }
+    }
 
     int num = readouts->n;              // Number of inputs
@@ -257,5 +283,5 @@
     }
 
-    if (!pmStackCombine(outRO, expRO, stack, maskVal | maskBad, maskSuspect, maskBad, 0, iter, combineRej,
+    if (!pmStackCombine(outRO, expRO, stack, maskBad, maskSuspect, maskBlank, 0, iter, combineRej,
                         combineSys, combineDiscard, useVariance, safe, rejected)) {
         psError(PS_ERR_UNKNOWN, false, "Unable to combine input readouts.");
