Index: /trunk/ppMerge/src/Makefile.am
===================================================================
--- /trunk/ppMerge/src/Makefile.am	(revision 15936)
+++ /trunk/ppMerge/src/Makefile.am	(revision 15937)
@@ -11,4 +11,9 @@
 	ppMergeData.c		\
 	ppMergeMask.c		\
+	ppMergeMaskSuspect.c		\
+	ppMergeMaskBad.c		\
+	ppMergeMaskWrite.c		\
+	ppMergeMaskGrow.c		\
+	ppMergeMaskAverageConcepts.c	\
 	ppMergeOptions.c	\
 	ppMergeScaleZero.c	\
Index: /trunk/ppMerge/src/ppMergeMask.c
===================================================================
--- /trunk/ppMerge/src/ppMergeMask.c	(revision 15936)
+++ /trunk/ppMerge/src/ppMergeMask.c	(revision 15937)
@@ -22,193 +22,30 @@
     )
 {
-    psArray *filenames = psMetadataLookupPtr(NULL, config->arguments, "INPUT"); // The input file names
-    assert(filenames);                  // It should be here --- it's put here in ppMergeConfig
-
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0); // Random number generator
-    pmFPA *fpaOut = data->out;          // Output FPA
-
-    // Iterate over each file
-    for (int i = 0; i < filenames->n; i++) {
-        if (! filenames->data[i] || strlen(filenames->data[i]) == 0) {
-            continue;
-        }
-        psFits *fits = data->files->data[i]; // FITS file handle
-        if (!fits) {
-            continue;
-        }
-        psTrace("ppMerge", 3, "File %d: %s\n", i, (const char*)filenames->data[i]);
-
-        pmFPA *fpaIn = data->in->data[i]; // Input FPA
-        pmFPAview *view = pmFPAviewAlloc(0); // View of FPA, for iteration
-        pmChip *chipIn;                 // Input chip of interest
-        while ((chipIn = pmFPAviewNextChip(view, fpaIn, 1))) {
-
-	    // handle chip vs cell statistics & avoid reading the data twice
-
-	    // load the data of all cells 
-            pmCell *cellIn;             // Input cell of interest
-            while ((cellIn = pmFPAviewNextCell(view, fpaIn, 1))) {
-                if (!pmCellRead(cellIn, fits, config->database)) continue;
-                if (cellIn->readouts->n == 0) continue;
-            }
-
-	    // calculate the readout statistics either for each readout, or across the entire chip
-	    if (options->statsByChip) {
-		ppMergeMaskChipStats (chipIn, options, rng);
-	    } else {
-		// calculate the stats for each cell independently
-		while ((cellIn = pmFPAviewNextCell(view, fpaIn, 1))) {
-		    if (cellIn->readouts->n == 0) continue;
-		    pmReadout *roIn;        // Input readout of interest
-		    while ((roIn = pmFPAviewNextReadout(view, fpaIn, 1))) {
-			if (!roIn->image) continue;
-			psTrace("ppMerge", 4, "Measure statistics for chip %d, cell %d, ro %d\n",
-				view->chip, view->cell, view->readout);
-			ppMergeMaskReadoutStats (roIn, options, rng);
-		    }
-		}
-	    }
-
-	    // apply the measured statistics to determine the outliers to be masked
-	    while ((cellIn = pmFPAviewNextCell(view, fpaIn, 1))) {
-		if (cellIn->readouts->n == 0) continue;
-
-		pmCell *cellOut = pmFPAviewThisCell(view, fpaOut); // Output cell
-		// Suspect pixels image
-		psImage *suspect = psMetadataLookupPtr(NULL, cellOut->analysis, "MASK.SUSPECT");
-		bool first = suspect ? false : true;
-
-		pmReadout *roIn;        // Input readout of interest
-		while ((roIn = pmFPAviewNextReadout(view, fpaIn, 1))) {
-		    if (!roIn->image) {
-			continue;
-		    }
-		    psTrace("ppMerge", 4, "Flagging suspect pixels in chip %d, cell %d, ro %d\n",
-			    view->chip, view->cell, view->readout);
-		    suspect = pmMaskFlagSuspectPixels(suspect, roIn, options->maskSuspect, options->combine->maskVal);
-		}
-
-		if (first) {
-		    psMetadataAddImage(cellOut->analysis, PS_LIST_TAIL, "MASK.SUSPECT", 0,
-				       "Suspect pixels", suspect);
-		    psFree(suspect);
-		}
-                pmCellFreeData(cellIn);
-            }
-            pmChipFreeData(chipIn);
-        }
-        pmFPAFreeData(fpaIn);
-        psFree(view);
-    }
-    psFree(rng);
-
-    pmFPAview *view = pmFPAviewAlloc(0);// View of FPA, for iteration
-    if (fpaOut->hdu) {
-        pmFPAUpdateNames(fpaOut, NULL, NULL);
-    }
-    pmFPAWriteMask(fpaOut, data->outFile, config->database, true, false); // Write header only
-    pmChip *chipOut;                    // Output chip of interest
-    while ((chipOut = pmFPAviewNextChip(view, fpaOut, 1))) {
-        if (chipOut->hdu) {
-            chipOut->data_exists = true;
-            pmFPAUpdateNames(fpaOut, chipOut, NULL);
-        }
-        pmChipWriteMask(chipOut, data->outFile, config->database, true, false); // Write header only
-        pmCell *cellOut;                   // Output cell of interest
-        while ((cellOut = pmFPAviewNextCell(view, fpaOut, 1))) {
-            if (cellOut->hdu) {
-                chipOut->data_exists = cellOut->data_exists = true;
-                pmFPAUpdateNames(fpaOut, chipOut, cellOut);
-            }
-            pmCellWriteMask(cellOut, data->outFile, config->database, true); // Write header only
-
-            psImage *suspect = psMetadataLookupPtr(NULL, cellOut->analysis, "MASK.SUSPECT");
-            if (! suspect) {
-                continue;
-            }
-
-            pmReadout *roOut = pmReadoutAlloc(cellOut); // Output readout
-            roOut->mask = pmMaskIdentifyBadPixels(suspect, options->combine->maskVal, filenames->n, options->maskBad, options->maskMode);
-            roOut->data_exists = cellOut->data_exists = chipOut->data_exists = true;
-
-            // Get list of cells for concepts averaging
-            {
-                psList *inCells = psListAlloc(NULL); // List of cells
-                for (int i = 0; i < filenames->n; i++) {
-                    if (! filenames->data[i] || strlen(filenames->data[i]) == 0) {
-                        continue;
-                    }
-                    pmCell *cellIn = pmFPAviewThisCell(view, data->in->data[i]); // Input cell
-                    psListAdd(inCells, PS_LIST_TAIL, cellIn);
-                }
-                if (!pmConceptsAverageCells(cellOut, inCells, NULL, NULL, true)) {
-                    psError(PS_ERR_UNKNOWN, false, "Unable to average cell concepts.");
-                    psFree(inCells);
-                    return false;
-                }
-                psFree(inCells);
-            }
-
-            // Statistics on the merged cell
-            if (data->statsFile) {
-                if (!data->stats) {
-                    data->stats = psMetadataAlloc();
-                }
-
-                // Build a fake image to do statistics
-                roOut->image = psImageAlloc(roOut->mask->numCols, roOut->mask->numRows, PS_TYPE_F32);
-                psImageInit(roOut->image, 1.0);
-                if (!ppStatsFPA(data->stats, data->out, view,
-                                options->combine->maskVal | pmConfigMask("BLANK", config),
-                                config)) {
-                    psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to generate stats for image.\n");
-                    return false;
-                }
-                psFree(roOut->image);
-                roOut->image = NULL;
-            }
-
-            psFree(roOut);              // Drop reference
-
-            if (cellOut->hdu && !cellOut->hdu->blankPHU) {
-                psTrace("ppMerge", 5, "Writing out cell HDU.\n");
-                pmCellWriteMask(cellOut, data->outFile, config->database, false);
-                pmCellFreeData(cellOut);
-            }
-        }
-
-        if (chipOut->hdu && !chipOut->hdu->blankPHU) {
-            psTrace("ppMerge", 5, "Writing out chip HDU.\n");
-            pmChipWriteMask(chipOut, data->outFile, config->database, false, false);
-            pmChipFreeData(chipOut);
-        }
-    }
-
-    // Get list of FPAs for concepts averaging
-    {
-        psList *inFPAs = psListAlloc(NULL); // List of FPAs
-        for (int i = 0; i < filenames->n; i++) {
-            if (! filenames->data[i] || strlen(filenames->data[i]) == 0) {
-                continue;
-            }
-            pmFPA *fpaIn = data->in->data[i]; // Input FPA
-            psListAdd(inFPAs, PS_LIST_TAIL, fpaIn);
-        }
-
-        if (!pmConceptsAverageFPAs(fpaOut, inFPAs)) {
-            psError(PS_ERR_UNKNOWN, false, "Unable to average FPA concepts.");
-            psFree(inFPAs);
-            return false;
-        }
-        psFree(inFPAs);
-    }
-
-    if (fpaOut->hdu && !fpaOut->hdu->blankPHU) {
-        psTrace("ppMerge", 5, "Writing out FPA HDU.\n");
-        pmFPAWriteMask(fpaOut, data->outFile, config->database, false, false);
-    }
-    pmFPAFreeData(fpaOut);
-
-    psFree(view);
+    for (int i = 0; i < 2; i++) {
+	if (!ppMergeMaskSuspect (data, options, config)) {
+	    psError(PS_ERR_UNKNOWN, true, "failed on mask suspect.\n");
+	    return false;
+	}
+
+	if (!ppMergeMaskBad (data, options, config)) {
+	    psError(PS_ERR_UNKNOWN, true, "failed on mask bad.\n");
+	    return false;
+	}
+    }
+
+    if (!ppMergeMaskAverageConcepts (data, options, config)) {
+	psError(PS_ERR_UNKNOWN, true, "failed on average concepts.\n");
+	return false;
+    }
+
+    if (!ppMergeMaskGrow (data, options, config)) {
+	psError(PS_ERR_UNKNOWN, true, "failed on mask grow.\n");
+	return false;
+    }
+
+    if (!ppMergeMaskWrite (data, options, config)) {
+	psError(PS_ERR_UNKNOWN, true, "failed on mask write.\n");
+	return false;
+    }
 
     return true;
@@ -216,4 +53,5 @@
 
 bool ppMergeMaskReadoutStats(const pmReadout *readout, 
+			     const pmReadout *output, 
 			     ppMergeOptions *options, // Options
 			     psRandom *rng)
@@ -229,6 +67,10 @@
     }
 
+    psImage *mask = NULL;
     psImage *image = readout->image;    // Image of interest
-    psImage *mask = readout->mask;      // Corresponding mask
+
+    if (output) {
+	mask = output->mask;      // Corresponding mask
+    }
 
     if (rng) {
@@ -263,4 +105,5 @@
 
 bool ppMergeMaskChipStats (const pmChip *chip,
+			   const pmChip *output, 
 			   ppMergeOptions *options,
 			   psRandom *rng)
@@ -291,5 +134,12 @@
 
 	    psImage *image = readout->image;    // Image of interest
-	    psImage *mask = readout->mask;      // Corresponding mask
+
+	    pmCell *cellOutput = output->cells->data[nCell];
+	    pmReadout *readoutOutput = NULL;
+	    psImage *mask = NULL;
+	    if (cellOutput->readouts->n > 0) {
+		readoutOutput = cellOutput->readouts->data[0];
+		mask = readoutOutput->mask;      // Corresponding mask
+	    }
 
 	    // Size of image
@@ -318,4 +168,12 @@
     }
 
+    // no valid data, skip the chip
+    if (!values->n) {
+	psFree(values);
+	psFree(stats);
+	psFree(rng);
+	return true;
+    }
+
     // calculate the statistics
     if (!psVectorStats (stats, values, NULL, NULL, 0)) {
@@ -327,5 +185,5 @@
     }
     if (!isfinite(stats->robustMedian) || !isfinite(stats->robustUQ) || !isfinite(stats->robustLQ)) {
-	psError(PS_ERR_UNKNOWN, false, "invalide image statistics (nan).\n");
+	psError(PS_ERR_UNKNOWN, false, "invalid image statistics (nan).\n");
 	psFree(values);
 	psFree(stats);
@@ -347,4 +205,6 @@
 	}
     }
+
+    psLogMsg ("ppMerge", PS_LOG_INFO, "statistics for chip: %f +/- %f\n", stats->robustMedian, stats->robustStdev);
 
     psFree(values);
Index: /trunk/ppMerge/src/ppMergeMask.h
===================================================================
--- /trunk/ppMerge/src/ppMergeMask.h	(revision 15936)
+++ /trunk/ppMerge/src/ppMergeMask.h	(revision 15937)
@@ -12,9 +12,38 @@
     );
 
+bool ppMergeMaskSuspect(ppMergeData *data,  // Data
+			ppMergeOptions *options, // Options
+			pmConfig *config    // Configuration
+    );
+
+bool ppMergeMaskBad(ppMergeData *data,  // Data
+		    ppMergeOptions *options, // Options
+		    pmConfig *config    // Configuration
+    );
+
+bool ppMergeMaskAverageConcepts(ppMergeData *data,  // Data
+				ppMergeOptions *options, // Options
+				pmConfig *config    // Configuration
+    );
+
+bool ppMergeMaskWrite(ppMergeData *data,  // Data
+		      ppMergeOptions *options, // Options
+		      pmConfig *config    // Configuration
+    );
+
+bool ppMergeMaskGrow(ppMergeData *data,  // Data
+		    ppMergeOptions *options, // Options
+		    pmConfig *config    // Configuration
+    );
+
+bool ppMergeMaskGrowReadout (pmReadout *readout, psMaskType maskVal);
+
 bool ppMergeMaskReadoutStats(const pmReadout *readout, 
+			     const pmReadout *output, 
 			     ppMergeOptions *options, // Options
 			     psRandom *rng);
 
 bool ppMergeMaskChipStats (const pmChip *chip,
+			   const pmChip *output, 
 			   ppMergeOptions *options,
 			   psRandom *rng);
Index: /trunk/ppMerge/src/ppMergeMaskAverageConcepts.c
===================================================================
--- /trunk/ppMerge/src/ppMergeMaskAverageConcepts.c	(revision 15937)
+++ /trunk/ppMerge/src/ppMergeMaskAverageConcepts.c	(revision 15937)
@@ -0,0 +1,78 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include <ppStats.h>
+
+#include "ppMerge.h"
+#include "ppMergeData.h"
+#include "ppMergeMask.h"
+
+
+// Generate a mask
+bool ppMergeMaskAverageConcepts(ppMergeData *data,  // Data
+				ppMergeOptions *options, // Options
+				pmConfig *config    // Configuration
+    )
+{
+    psArray *filenames = psMetadataLookupPtr(NULL, config->arguments, "INPUT"); // The input file names
+    assert(filenames);                  // It should be here --- it's put here in ppMergeConfig
+
+    pmFPA *fpaOut = data->out;          // Output FPA
+
+    pmFPAview *view = pmFPAviewAlloc(0);// View of FPA, for iteration
+    if (fpaOut->hdu) {
+        pmFPAUpdateNames(fpaOut, NULL, NULL);
+    }
+
+    pmChip *chipOut;                    // Output chip of interest
+    while ((chipOut = pmFPAviewNextChip(view, fpaOut, 1))) {
+
+        pmCell *cellOut;                   // Output cell of interest
+        while ((cellOut = pmFPAviewNextCell(view, fpaOut, 1))) {
+
+            // Get list of cells for concepts averaging
+	    psList *inCells = psListAlloc(NULL); // List of cells
+	    for (int i = 0; i < filenames->n; i++) {
+		if (! filenames->data[i] || strlen(filenames->data[i]) == 0) {
+		    continue;
+		}
+		pmCell *cellIn = pmFPAviewThisCell(view, data->in->data[i]); // Input cell
+		psListAdd(inCells, PS_LIST_TAIL, cellIn);
+	    }
+	    if (!pmConceptsAverageCells(cellOut, inCells, NULL, NULL, true)) {
+		psError(PS_ERR_UNKNOWN, false, "Unable to average cell concepts.");
+		psFree(inCells);
+		return false;
+	    }
+	    psFree(inCells);
+        }
+    }
+
+    // Get list of FPAs for concepts averaging
+    psList *inFPAs = psListAlloc(NULL); // List of FPAs
+    for (int i = 0; i < filenames->n; i++) {
+	if (! filenames->data[i] || strlen(filenames->data[i]) == 0) {
+	    continue;
+	}
+	pmFPA *fpaIn = data->in->data[i]; // Input FPA
+	psListAdd(inFPAs, PS_LIST_TAIL, fpaIn);
+    }
+
+    if (!pmConceptsAverageFPAs(fpaOut, inFPAs)) {
+	psError(PS_ERR_UNKNOWN, false, "Unable to average FPA concepts.");
+	psFree(inFPAs);
+	return false;
+    }
+    psFree(inFPAs);
+
+    psFree(view);
+
+    return true;
+}
Index: /trunk/ppMerge/src/ppMergeMaskBad.c
===================================================================
--- /trunk/ppMerge/src/ppMergeMaskBad.c	(revision 15937)
+++ /trunk/ppMerge/src/ppMergeMaskBad.c	(revision 15937)
@@ -0,0 +1,83 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include <ppStats.h>
+
+#include "ppMerge.h"
+#include "ppMergeData.h"
+#include "ppMergeMask.h"
+
+
+// Generate a mask
+bool ppMergeMaskBad(ppMergeData *data,  // Data
+		    ppMergeOptions *options, // Options
+		    pmConfig *config    // Configuration
+    )
+{
+    psArray *filenames = psMetadataLookupPtr(NULL, config->arguments, "INPUT"); // The input file names
+    assert(filenames);                  // It should be here --- it's put here in ppMergeConfig
+
+    pmFPA *fpaOut = data->out;          // Output FPA
+
+    pmFPAview *view = pmFPAviewAlloc(0);// View of FPA, for iteration
+
+    pmChip *chipOut;                    // Output chip of interest
+    while ((chipOut = pmFPAviewNextChip(view, fpaOut, 1))) {
+
+        pmCell *cellOut;                   // Output cell of interest
+        while ((cellOut = pmFPAviewNextCell(view, fpaOut, 1))) {
+
+            psImage *suspect = psMetadataLookupPtr(NULL, cellOut->analysis, "MASK.SUSPECT");
+            if (! suspect) {
+                continue;
+            }
+
+	    pmReadout *roOut = NULL;
+	    if (cellOut->readouts->n > 0) {
+		roOut = psMemIncrRefCounter (cellOut->readouts->data[0]);
+		psFree (roOut->mask);
+		psTrace("ppMerge", 5, "save results with mask from previous pass\n");
+	    } else {
+		roOut = pmReadoutAlloc(cellOut); // Output readout
+	    }
+
+            roOut->mask = pmMaskIdentifyBadPixels(suspect, options->combine->maskVal, filenames->n, options->maskBad, options->maskMode);
+            roOut->data_exists = cellOut->data_exists = chipOut->data_exists = true;
+
+            // Statistics on the merged cell
+            if (data->statsFile) {
+                if (!data->stats) {
+                    data->stats = psMetadataAlloc();
+                }
+
+                // Build a fake image to do statistics
+                roOut->image = psImageAlloc(roOut->mask->numCols, roOut->mask->numRows, PS_TYPE_F32);
+                psImageInit(roOut->image, 1.0);
+                if (!ppStatsFPA(data->stats, data->out, view,
+                                options->combine->maskVal | pmConfigMask("BLANK", config),
+                                config)) {
+                    psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to generate stats for image.\n");
+                    return false;
+                }
+                psFree(roOut->image);
+                roOut->image = NULL;
+            }
+            psFree(roOut);              // Drop reference
+
+	    // drop this reference (unless??) 
+	    psMetadataRemoveKey (cellOut->analysis, "MASK.SUSPECT");
+        }
+    }
+
+    psFree(view);
+
+    return true;
+}
+
Index: /trunk/ppMerge/src/ppMergeMaskGrow.c
===================================================================
--- /trunk/ppMerge/src/ppMergeMaskGrow.c	(revision 15937)
+++ /trunk/ppMerge/src/ppMergeMaskGrow.c	(revision 15937)
@@ -0,0 +1,86 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include <ppStats.h>
+
+#include "ppMerge.h"
+#include "ppMergeData.h"
+#include "ppMergeMask.h"
+
+
+// Generate a mask
+bool ppMergeMaskGrow(ppMergeData *data,  // Data
+		    ppMergeOptions *options, // Options
+		    pmConfig *config    // Configuration
+    )
+{
+    pmFPA *fpaOut = data->out;          // Output FPA
+
+    pmFPAview *view = pmFPAviewAlloc(0);// View of FPA, for iteration
+
+    pmChip *chipOut;                    // Output chip of interest
+    while ((chipOut = pmFPAviewNextChip(view, fpaOut, 1))) {
+
+        pmCell *cellOut;                   // Output cell of interest
+        while ((cellOut = pmFPAviewNextCell(view, fpaOut, 1))) {
+
+	    pmReadout *roOut;
+	    while ((roOut = pmFPAviewNextReadout(view, fpaOut, 1))) {
+
+		ppMergeMaskGrowReadout(roOut, options->combine->maskVal);
+
+	    }
+        }
+    }
+
+    psFree(view);
+
+    return true;
+}
+
+# define NGROW 2
+
+bool ppMergeMaskGrowReadout (pmReadout *readout, psMaskType maskVal) {
+
+    if (!readout->mask) return true;
+
+    psImage *oldMask = readout->mask;
+    psImage *newMask = psImageCopy (NULL, oldMask, PS_TYPE_U8);
+
+    psImageInit (newMask, 0);
+
+    int Nx = oldMask->numCols;
+    int Ny = oldMask->numRows;
+
+    for (int iy = 0; iy < Ny; iy++) {
+	for (int ix = 0; ix < Nx; ix++) {
+
+	    if (oldMask->data.U8[iy][ix] && maskVal) {
+
+		for (int jy = -NGROW; jy <= +NGROW; jy++) {
+		    int ny = iy + jy;
+		    if (ny < 0) continue;
+		    if (ny >= Ny) continue;
+		    for (int jx = -NGROW; jx <= +NGROW; jx++) {
+			int nx = ix + jx;
+			if (nx < 0) continue;
+			if (nx >= Nx) continue;
+			newMask->data.U8[ny][nx] |= oldMask->data.U8[iy][ix];
+		    }
+		}
+	    }
+	}
+    }
+
+    psFree (readout->mask);
+    readout->mask = newMask;
+
+    return true;
+}
Index: /trunk/ppMerge/src/ppMergeMaskSuspect.c
===================================================================
--- /trunk/ppMerge/src/ppMergeMaskSuspect.c	(revision 15937)
+++ /trunk/ppMerge/src/ppMergeMaskSuspect.c	(revision 15937)
@@ -0,0 +1,117 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include <ppStats.h>
+
+#include "ppMerge.h"
+#include "ppMergeData.h"
+#include "ppMergeMask.h"
+
+
+// Generate a mask
+bool ppMergeMaskSuspect(ppMergeData *data,  // Data
+			ppMergeOptions *options, // Options
+			pmConfig *config    // Configuration
+    )
+{
+    psArray *filenames = psMetadataLookupPtr(NULL, config->arguments, "INPUT"); // The input file names
+    assert(filenames);                  // It should be here --- it's put here in ppMergeConfig
+
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0); // Random number generator
+    pmFPA *fpaOut = data->out;          // Output FPA
+
+    // Iterate over each file
+    for (int i = 0; i < filenames->n; i++) {
+        if (! filenames->data[i] || strlen(filenames->data[i]) == 0) {
+            continue;
+        }
+        psFits *fits = data->files->data[i]; // FITS file handle
+        if (!fits) {
+            continue;
+        }
+        psTrace("ppMerge", 3, "File %d: %s\n", i, (const char*)filenames->data[i]);
+
+        pmFPA *fpaIn = data->in->data[i]; // Input FPA
+        pmFPAview *view = pmFPAviewAlloc(0); // View of FPA, for iteration
+        pmChip *chipIn;                 // Input chip of interest
+        while ((chipIn = pmFPAviewNextChip(view, fpaIn, 1))) {
+
+	    // handle chip vs cell statistics & avoid reading the data twice
+
+	    // load the data of all cells 
+            pmCell *cellIn;             // Input cell of interest
+            while ((cellIn = pmFPAviewNextCell(view, fpaIn, 1))) {
+                if (!pmCellRead(cellIn, fits, config->database)) continue;
+                if (cellIn->readouts->n == 0) continue;
+            }
+
+	    // calculate the readout statistics either for each readout, or across the entire chip
+	    if (options->statsByChip) {
+		pmChip *chipOut = pmFPAviewThisChip(view, fpaOut); // Output cell
+		if (!ppMergeMaskChipStats (chipIn, chipOut, options, rng)) {
+		    psAbort ("stats problem");
+		}
+	    } else {
+		// calculate the stats for each cell independently
+		while ((cellIn = pmFPAviewNextCell(view, fpaIn, 1))) {
+		    if (cellIn->readouts->n == 0) continue;
+		    pmCell *cellOut = pmFPAviewThisCell(view, fpaOut); // Output cell
+
+		    pmReadout *roOut = NULL;
+		    if (cellOut->readouts->n > 0) {
+			roOut = cellOut->readouts->data[0];
+			psTrace("ppMerge", 5, "masking with results from previous pass\n");
+		    }
+
+		    pmReadout *roIn;        // Input readout of interest
+		    while ((roIn = pmFPAviewNextReadout(view, fpaIn, 1))) {
+			if (!roIn->image) continue;
+			psTrace("ppMerge", 4, "Measure statistics for chip %d, cell %d, ro %d\n",
+				view->chip, view->cell, view->readout);
+			ppMergeMaskReadoutStats (roIn, roOut, options, rng);
+		    }
+		}
+	    }
+
+	    // apply the measured statistics to determine the outliers to be masked
+	    while ((cellIn = pmFPAviewNextCell(view, fpaIn, 1))) {
+		if (cellIn->readouts->n == 0) continue;
+
+		pmCell *cellOut = pmFPAviewThisCell(view, fpaOut); // Output cell
+		// Suspect pixels image
+		psImage *suspect = psMetadataLookupPtr(NULL, cellOut->analysis, "MASK.SUSPECT");
+		bool first = suspect ? false : true;
+
+		pmReadout *roIn;        // Input readout of interest
+		while ((roIn = pmFPAviewNextReadout(view, fpaIn, 1))) {
+		    if (!roIn->image) {
+			continue;
+		    }
+		    psTrace("ppMerge", 4, "Flagging suspect pixels in chip %d, cell %d, ro %d\n",
+			    view->chip, view->cell, view->readout);
+		    suspect = pmMaskFlagSuspectPixels(suspect, roIn, options->maskSuspect, options->combine->maskVal);
+		}
+
+		if (first) {
+		    psMetadataAddImage(cellOut->analysis, PS_LIST_TAIL, "MASK.SUSPECT", 0,
+				       "Suspect pixels", suspect);
+		    psFree(suspect);
+		}
+                pmCellFreeData(cellIn);
+            }
+            pmChipFreeData(chipIn);
+        }
+        pmFPAFreeData(fpaIn);
+        psFree(view);
+    }
+    psFree(rng);
+
+    return true;
+}
Index: /trunk/ppMerge/src/ppMergeMaskWrite.c
===================================================================
--- /trunk/ppMerge/src/ppMergeMaskWrite.c	(revision 15937)
+++ /trunk/ppMerge/src/ppMergeMaskWrite.c	(revision 15937)
@@ -0,0 +1,69 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include <ppStats.h>
+
+#include "ppMerge.h"
+#include "ppMergeData.h"
+#include "ppMergeMask.h"
+
+
+// Generate a mask
+bool ppMergeMaskWrite(ppMergeData *data,  // Data
+		      ppMergeOptions *options, // Options
+		      pmConfig *config    // Configuration
+    )
+{
+    pmFPA *fpaOut = data->out;          // Output FPA
+
+    pmFPAview *view = pmFPAviewAlloc(0);// View of FPA, for iteration
+    if (fpaOut->hdu) {
+        pmFPAUpdateNames(fpaOut, NULL, NULL);
+    }
+    pmFPAWriteMask(fpaOut, data->outFile, config->database, true, false); // Write header only
+    pmChip *chipOut;                    // Output chip of interest
+    while ((chipOut = pmFPAviewNextChip(view, fpaOut, 1))) {
+        if (chipOut->hdu) {
+            chipOut->data_exists = true;
+            pmFPAUpdateNames(fpaOut, chipOut, NULL);
+        }
+        pmChipWriteMask(chipOut, data->outFile, config->database, true, false); // Write header only
+        pmCell *cellOut;                   // Output cell of interest
+        while ((cellOut = pmFPAviewNextCell(view, fpaOut, 1))) {
+            if (cellOut->hdu) {
+                chipOut->data_exists = cellOut->data_exists = true;
+                pmFPAUpdateNames(fpaOut, chipOut, cellOut);
+            }
+            pmCellWriteMask(cellOut, data->outFile, config->database, true); // Write header only
+
+            if (cellOut->hdu && !cellOut->hdu->blankPHU) {
+                psTrace("ppMerge", 5, "Writing out cell HDU.\n");
+                pmCellWriteMask(cellOut, data->outFile, config->database, false);
+                pmCellFreeData(cellOut);
+            }
+        }
+
+        if (chipOut->hdu && !chipOut->hdu->blankPHU) {
+            psTrace("ppMerge", 5, "Writing out chip HDU.\n");
+            pmChipWriteMask(chipOut, data->outFile, config->database, false, false);
+            pmChipFreeData(chipOut);
+        }
+    }
+
+    if (fpaOut->hdu && !fpaOut->hdu->blankPHU) {
+        psTrace("ppMerge", 5, "Writing out FPA HDU.\n");
+        pmFPAWriteMask(fpaOut, data->outFile, config->database, false, false);
+    }
+    pmFPAFreeData(fpaOut);
+
+    psFree(view);
+
+    return true;
+}
