Index: /trunk/archive/scripts/src/phase2/Makefile
===================================================================
--- /trunk/archive/scripts/src/phase2/Makefile	(revision 5103)
+++ /trunk/archive/scripts/src/phase2/Makefile	(revision 5104)
@@ -5,10 +5,12 @@
 DEVFLAGS += -DTESTING		# Testing version
 
-CFLAGS += -O0 -g -std=c99 -Werror -I/home/mithrandir/price/pan-starrs/jhroot/i686-pc-linux-gnu/include/ -D_GNU_SOURCE $(DEVFLAGS)
-PSLIB += -L/home/mithrandir/price/pan-starrs/jhroot/i686-pc-linux-gnu/lib/ -lpslib -lgsl -lgslcblas -lfftw3f -lsla -lcfitsio -lm -lxml2 -lmysqlclient
-PSMODULE += -L/home/mithrandir/price/pan-starrs/jhroot/i686-pc-linux-gnu/lib/ -lpsmodule
+CFLAGS += -O0 -g -std=c99 -Werror -I$(JHBUILD)/include/ -I$(JHBUILD)/include/libxml2/ -DPS_NO_TRACE -D_GNU_SOURCE $(DEVFLAGS)
+PSLIB += -L$(JHBUILD)/lib/ -lpslib -lgsl -lgslcblas -lfftw3f -lsla -lcfitsio -lm -L$(JHBUILD)/lib/libxml2/ -L$(JHBUILD)/lib/mysql/ -lxml2 -lmysqlclient
+PSMODULE += -L$(JHBUILD)/lib/ -lpsmodule
 LDFLAGS += $(PSLIB)
 
-OBJS = papPhase2.o psAdditionals.o pmConfig.o pmFPA.o pmFPAConstruct.o papStuff.o
+OBJS = papPhase2.o psAdditionals.o pmConfig.o pmFPA.o pmFPAConstruct.o papStuff.o pmFPARead.o                \
+	pmFPAConceptsGet.o pmFPAConceptsSet.o pmFPAWrite.o                                                   \
+	pmFlatField.o pmMaskBadPixels.o pmNonLinear.o pmSubtractBias.o
 TARGET = papPhase2
 
Index: /trunk/archive/scripts/src/phase2/papPhase2.c
===================================================================
--- /trunk/archive/scripts/src/phase2/papPhase2.c	(revision 5103)
+++ /trunk/archive/scripts/src/phase2/papPhase2.c	(revision 5104)
@@ -4,10 +4,16 @@
 #include "pslib.h"
 #include "psAdditionals.h"
-
-#include "pmSubtractBias.h"
 
 #include "pmFPA.h"
 #include "pmConfig.h"
 #include "pmFPAConstruct.h"
+#include "pmFPARead.h"
+#include "pmFPAConceptsGet.h"
+#include "pmFPAWrite.h"
+
+#include "pmFlatField.h"
+#include "pmMaskBadPixels.h"
+#include "pmNonLinear.h"
+#include "pmSubtractBias.h"
 
 // Phase 2 needs to:
@@ -37,5 +43,5 @@
 #define RECIPE "PHASE2"			// Name of the recipe to use
 
-static psMemoryId memPrintAlloc(const psMemBlock *mb)
+static psMemId memPrintAlloc(const psMemBlock *mb)
 {
     printf("Allocated memory block %lld (%lld):\n"
@@ -46,5 +52,5 @@
     return 0;
 }
-static psMemoryId memPrintFree(const psMemBlock *mb)
+static psMemId memPrintFree(const psMemBlock *mb)
 {
     printf("Freed memory block %lld (%lld):\n"
@@ -69,11 +75,11 @@
 #if 0
     // Hunting memory leaks
-    psMemAllocateCallbackSetID(3539);
-    psMemFreeCallbackSetID(3539);
+    psMemAllocateCallbackSetID(23289);
+    psMemFreeCallbackSetID(23289);
     psMemAllocateCallbackSet(memPrintAlloc);
     psMemFreeCallbackSet(memPrintFree);
 #endif
 
-//    psTraceSetLevel(".", 10);
+    psTraceSetLevel("pmConfigCameraFromHeader", 10);
 
     // Parse the configurations
@@ -88,9 +94,9 @@
     // Parse other command-line arguments
     psMetadata *arguments = psMetadataAlloc(); // The arguments, with default values
-    psMetadataAddStr(arguments, PS_LIST_TAIL, "-bias", "Name of the bias image", "");
-    psMetadataAddStr(arguments, PS_LIST_TAIL, "-dark", "Name of the dark image", "");
-    psMetadataAddStr(arguments, PS_LIST_TAIL, "-flat", "Name of the flat-field image", "");
-    psMetadataAddStr(arguments, PS_LIST_TAIL, "-mask", "Name of the mask image", "");
-    psMetadataAddS32(arguments, PS_LIST_TAIL, "-chip", "Chip number to process (if positive)", -1);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-bias", 0, "Name of the bias image", "");
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-dark", 0, "Name of the dark image", "");
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-flat", 0, "Name of the flat-field image", "");
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-mask", 0, "Name of the mask image", "");
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-chip", 0, "Chip number to process (if positive)", -1);
     if (! psArgumentParse(arguments, &argc, argv) || argc != 3) {
 	printf("\nPan-STARRS Phase 2 processing\n\n");
@@ -113,5 +119,4 @@
     printf("Mask: %s\n", maskName);
     printf("Chip: %d\n", chipNum);
-    psFree(arguments);
 
     // Open the input
@@ -132,5 +137,4 @@
 #endif
 
-#if 0
     // Open the output and output mask
     // We do it here so that we don't process the whole lot and then find out we can't open the output file
@@ -144,4 +148,6 @@
 	exit(EXIT_FAILURE);
     }
+
+#if 0
     psString outputMaskName = psStringCopy(outputName);
     (void)psStringAppend(&outputMaskName, ".mask");
@@ -158,5 +164,4 @@
 #endif
 
-	
     // Get camera configuration from header if not already defined
     if (! camera) {
@@ -176,9 +181,9 @@
 
     // Construct camera in preparation for reading
-    pmFPA *input = pmFPAConstruct(camera, database);
-    pmFPA *mask = pmFPAConstruct(camera, database);
-    pmFPA *bias = pmFPAConstruct(camera, database);
-    pmFPA *dark = pmFPAConstruct(camera, database);
-    pmFPA *flat = pmFPAConstruct(camera, database);
+    pmFPA *input = pmFPAConstruct(camera);
+    pmFPA *mask = pmFPAConstruct(camera);
+    pmFPA *bias = pmFPAConstruct(camera);
+    pmFPA *dark = pmFPAConstruct(camera);
+    pmFPA *flat = pmFPAConstruct(camera);
 
     // Set various tasks
@@ -198,5 +203,5 @@
 
     if (psMetadataLookupBool(NULL, recipe, "MASK")) {
-	if (maskName) {
+	if (strlen(maskName) > 0) {
 	    doMask = true;
 	} else {
@@ -209,5 +214,5 @@
     }
     if (psMetadataLookupBool(NULL, recipe, "BIAS")) {
-	if (biasName) {
+	if (strlen(biasName) > 0) {
 	    doBias = true;
 	} else {
@@ -217,5 +222,5 @@
     }
     if (psMetadataLookupBool(NULL, recipe, "DARK")) {
-	if (darkName) {
+	if (strlen(darkName) > 0) {
 	    doDark = true;
 	} else {
@@ -260,6 +265,7 @@
 	}
     }
+
     if (psMetadataLookupBool(NULL, recipe, "FLAT")) {
-	if (flatName) {
+	if (strlen(flatName) > 0) {
 	    doFlat = true;
 	} else {
@@ -269,5 +275,4 @@
     }
 
-#if 0
     // Chip selection
     if (chipNum >= 0) {
@@ -282,5 +287,5 @@
 
     // Read in the input pixels
-    if (! pmFPARead(input, inputFile)) {
+    if (! pmFPARead(input, inputFile, header, database)) {
 	psErrorStackPrint(stderr, "Unable to populate camera from FITS file: %s\n", inputName);
 	exit(EXIT_FAILURE);
@@ -292,67 +297,100 @@
 #endif
 
+    pmFPAPrint(input);
 
     // Load the calibration frames, if required
-    if (biasName) {
+    if (doBias) {
+#ifdef PRODUCTION
 	psFits *biasFile = psFitsOpen(biasName, "r"); // File handle for bias
+#else
+	psFits *biasFile = psFitsAlloc(biasName);
+#endif
 	psMetadata *biasHeader = psFitsReadHeader(NULL, biasFile); // FITS header for bias
 	if (! pmConfigValidateCamera(camera, biasHeader)) {
-	    psError("phase2", true, "Bias (%s) does not seem to be from the same camera.\n", biasName);
+	    psError(PS_ERR_IO, true, "Bias (%s) does not seem to be from the same camera.\n", biasName);
 	    exit(EXIT_FAILURE);
 	}
 	psFree(biasHeader);
-	if (! pmFPARead(bias, biasFile)) {
+	if (! pmFPARead(bias, biasFile, NULL, database)) {
 	    psErrorStackPrint(stderr, "Unable to populate bias camera from fits FITS: %s\n", biasName);
 	    exit(EXIT_FAILURE);
 	}
+#ifdef PRODUCTION
 	psFitsClose(biasFile);
-    }
-
-    if (darkName) {
+#else
+	psFree(biasFile);
+#endif
+    }
+
+    if (doDark) {
+#ifdef PRODUCTION
 	psFits *darkFile = psFitsOpen(darkName, "r"); // File handle for dark
+#else
+	psFits *darkFile = psFitsAlloc(darkName);
+#endif
 	psMetadata *darkHeader = psFitsReadHeader(NULL, darkFile); // FITS header for dark
 	if (! pmConfigValidateCamera(camera, darkHeader)) {
-	    psError("phase2", true, "Dark (%s) does not seem to be from the same camera.\n", darkName);
+	    psError(PS_ERR_IO, true, "Dark (%s) does not seem to be from the same camera.\n", darkName);
 	    exit(EXIT_FAILURE);
 	}
 	psFree(darkHeader);
-	if (! pmFPARead(dark, darkFile)) {
+	if (! pmFPARead(dark, darkFile, NULL, database)) {
 	    psErrorStackPrint(stderr, "Unable to populate dark camera from fits FITS: %s\n", darkName);
 	    exit(EXIT_FAILURE);
 	}
+#ifdef PRODUCTION
 	psFitsClose(darkFile);
-    }
-
-    if (maskName) {
+#else
+	psFree(darkFile);
+#endif
+    }
+
+    if (doMask) {
+#ifdef PRODUCTION
 	psFits *maskFile = psFitsOpen(maskName, "r"); // File handle for mask
+#else
+	psFits *maskFile = psFitsAlloc(maskName);
+#endif
 	psMetadata *maskHeader = psFitsReadHeader(NULL, maskFile); // FITS header for mask
 	if (! pmConfigValidateCamera(camera, maskHeader)) {
-	    psError("phase2", true, "Mask (%s) does not seem to be from the same camera.\n", maskName);
+	    psError(PS_ERR_IO, true, "Mask (%s) does not seem to be from the same camera.\n", maskName);
 	    exit(EXIT_FAILURE);
 	}
 	psFree(maskHeader);
-	if (! pmFPARead(mask, maskFile)) {
+	if (! pmFPARead(mask, maskFile, NULL, database)) {
 	    psErrorStackPrint(stderr, "Unable to populate mask camera from fits FITS: %s\n", maskName);
 	    exit(EXIT_FAILURE);
 	}
+#ifdef PRODUCTION
 	psFitsClose(maskFile);
+#else
+	psFree(maskFile);
+#endif
     }
     
-    if (flatName) {
+    if (doFlat) {
+#ifdef PRODUCTION
 	psFits *flatFile = psFitsOpen(flatName, "r"); // File handle for flat
+#else
+	psFits *flatFile = psFitsAlloc(flatName);
+#endif
 	psMetadata *flatHeader = psFitsReadHeader(NULL, flatFile); // FITS header for flat
 	if (! pmConfigValidateCamera(camera, flatHeader)) {
-	    psError("phase2", true, "Flat (%s) does not seem to be from the same camera.\n", flatName);
+	    psError(PS_ERR_IO, true, "Flat (%s) does not seem to be from the same camera.\n", flatName);
 	    exit(EXIT_FAILURE);
 	}
 	psFree(flatHeader);
-	if (! pmFPARead(flat, flatFile)) {
+	if (! pmFPARead(flat, flatFile, NULL, database)) {
 	    psErrorStackPrint(stderr, "Unable to populate flat camera from fits FITS: %s\n", flatName);
 	    exit(EXIT_FAILURE);
 	}
+#ifdef PRODUCTION
 	psFitsClose(flatFile);
+#else
+	psFree(flatFile);
+#endif
     }
     
-    psArray *inputChips = inputs->chips; // Array of chips in input image
+    psArray *inputChips = input->chips; // Array of chips in input image
     psArray *biasChips = bias->chips;	// Array of chips in bias image
     psArray *darkChips = dark->chips;	// Array of chips in dark image
@@ -377,9 +415,9 @@
 
 	for (int j = 0; j < inputCells->n; j++) {
-	    pmCell *inputCell = inputCells->data[i]; // Cell of interest in input image
-	    pmCell *biasCell = biasCells->data[i]; // Cell of interest in bias image
-	    pmCell *darkCell = darkCells->data[i]; // Cell of interest in dark imag
-	    pmCell *maskCell = maskCells->data[i]; // Cell of interest in mask image
-	    pmCell *flatCell = flatCells->data[i]; // Cell of interest in flat image
+	    pmCell *inputCell = inputCells->data[j]; // Cell of interest in input image
+	    pmCell *biasCell = biasCells->data[j]; // Cell of interest in bias image
+	    pmCell *darkCell = darkCells->data[j]; // Cell of interest in dark imag
+	    pmCell *maskCell = maskCells->data[j]; // Cell of interest in mask image
+	    pmCell *flatCell = flatCells->data[j]; // Cell of interest in flat image
 
 	    if (! inputCell->valid) {
@@ -388,33 +426,37 @@
 
 	    psArray *inputReadouts = inputCell->readouts; // Array of readouts in input image
-	    if (biasCell->readouts->n != 1) {
+	    if (doBias && biasCell->readouts->n > 1) {
 		psLogMsg("phase2", PS_LOG_WARN, "Bias contains multiple readouts: only the first will be"
 			 " used.");
 	    }
-	    if (darkCell->readouts->n != 1) {
+	    if (doDark && darkCell->readouts->n > 1) {
 		psLogMsg("phase2", PS_LOG_WARN, "Dark contains multiple readouts: only the first will be"
 			 " used.");
 	    }
-	    if (maskCell->readouts->n != 1) {
+	    if (doMask && maskCell->readouts->n > 1) {
 		psLogMsg("phase2", PS_LOG_WARN, "Mask contains multiple readouts: only the first will be"
 			 " used.");
 	    }
-	    if (flatCell->readouts->n != 1) {
+	    if (doFlat && flatCell->readouts->n > 1) {
 		psLogMsg("phase2", PS_LOG_WARN, "Flat contains multiple readouts: only the first will be"
 			 " used.");
 	    }
 
+	    pmReadout *biasReadout = NULL; // Bias readout
+	    pmReadout *maskReadout = NULL; // Mask readout
+	    pmReadout *flatReadout = NULL; // Flat readout
 	    psImage *biasImage = NULL;	// Bias pixels
 	    psImage *darkImage = NULL;	// Dark pixels
 	    float darkTime = 1.0;	// Dark time for dark image
-
-	    if (biasName) {
-		pmReadout *biasReadout = biasCell->readouts->data[0]; // Readout of interest in bias image
+	    psImage *maskImage = NULL;	// Mask pixels
+
+	    if (doBias) {
+		biasReadout = biasCell->readouts->data[0]; // Readout of interest in bias image
 		biasImage = biasReadout->image;
 	    }
-	    if (darkName) {
+	    if (doDark) {
 		pmReadout *darkReadout = darkCell->readouts->data[0]; // Readout of interest in dark image
 		darkImage = darkReadout->image;
-		darkTime = pmReadoutGetDarkTime(darkReadout);
+		darkTime = psMetadataLookupF32(NULL, darkCell->concepts, "CELL.DARKTIME");
 		if (darkTime <= 0.0) {
 		    psErrorStackPrint(stderr, "DARKTIME for dark image (%f) is non-positive.\n", darkTime);
@@ -422,9 +464,10 @@
 		}
 	    }
-	    if (maskName) {
-		pmReadout *maskReadout = maskCell->readouts->data[0]; // Readout of interest in mask image
-	    }
-	    if (flatName) {
-		pmReadout *flatReadout = flatCell->readouts->data[0]; // Readout of interest in mask image
+	    if (doMask) {
+		maskReadout = maskCell->readouts->data[0]; // Readout of interest in mask image
+		maskImage = maskReadout->image;
+	    }
+	    if (doFlat) {
+		flatReadout = flatCell->readouts->data[0]; // Readout of interest in mask image
 	    }
 
@@ -432,12 +475,14 @@
 		pmReadout *inputReadout = inputReadouts->data[k]; // Readout of interest in input image
 		psImage *inputImage = inputReadout->image; // The actual image
-		psList *inputBias = inputReadout->bias; // List of overscan bias regions
+		psList *inputOverscans = inputReadout->overscans; // List of overscan bias regions
 
 		// Mask bad pixels
 		if (doMask) {
+		    float saturation = psMetadataLookupF32(NULL, inputCell->concepts, "CELL.SATURATION");
+
 		    // Need to change this later to grow the mask by the size of the convolution kernel
-		    (void)pmMaskBadPixels(inputReadout, maskReadout,
-					  PS_MASK_TRAP | PS_MASK_BADCOL | PS_MASK_SAT, saturation,
-					  PS_MASK_TRAP, 0);
+		    (void)pmMaskBadPixels(inputReadout, maskImage,
+					  PM_MASK_TRAP | PM_MASK_BADCOL | PM_MASK_SAT, saturation,
+					  PM_MASK_TRAP, 0);
 		}
 
@@ -450,91 +495,106 @@
 		    } else {
 			switch (dataItem->type) {
-			  case PS_META_VECTOR:
-			    // These are the polynomial coefficients
-			    psVector *coeff = dataItem->data.V; // The coefficient vector
-			    if (coeff->type.type != PS_TYPE_F64) {
-				psVector *temp = psVectorCopy(NULL, coeff, PS_TYPE_F64); // F64 version
+			  case PS_META_VEC:
+			    {
+				// These are the polynomial coefficients
+				psVector *coeff = dataItem->data.V; // The coefficient vector
+				if (coeff->type.type != PS_TYPE_F64) {
+				    psVector *temp = psVectorCopy(NULL, coeff, PS_TYPE_F64); // F64 version
+				    psFree(coeff);
+				    coeff = temp;
+				}
+				psPolynomial1D *correction = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD,
+										 coeff->n + 1);
+				for (int i = 0; i < coeff->n; i++) {
+				    correction->coeff[i] = coeff->data.F64[i];
+				}
+				(void)pmNonLinearityPolynomial(inputReadout, correction);
 				psFree(coeff);
-				coeff = temp;
+				psFree(correction);
 			    }
-			    psPolynomial1D *correction = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD,
-									     coeff->n + 1);
-			    for (int i = 0; i < coeff->n; i++) {
-				correction->coeff[i] = coeff->data.F64[i];
-			    }
-			    (void)pmNonLinearityPolynomial(inputReadout, correction);
-			    psFree(coeffCopy);
-			    psFree(coeff);
-			    psFree(correction);
 			    break;
 			  case PS_META_STR:
-			    // This is a filename
-			    psString name = dataItem->data.V;	// Filename
-			    psLookupTable *table = psLookupTableAlloc(name, "%f %f", 0);
-			    if (psLookupTableRead(table) <= 0) {
-				psErrorStackPrint(stderr, "Unable to read non-linearity correction file %s "
-						  "--- ignored\n", tableName);
-			    } else {
-				(void)pmNonLinearityLookup(inputReadout, table);
+			    {
+				// This is a filename
+				psString name = dataItem->data.V;	// Filename
+				psLookupTable *table = psLookupTableAlloc(name, "%f %f", 0);
+				if (psLookupTableRead(table) <= 0) {
+				    psErrorStackPrint(stderr, "Unable to read non-linearity correction file "
+						      "%s --- ignored\n", name);
+				} else {
+#ifdef PRODUCTION
+				    (void)pmNonLinearityLookup(inputReadout, table);
+#else
+				    printf("XXX: Non-linearity with lookup table not yet implemented.\n");
+#endif
+				}
+				psFree(table);
 			    }
-			    psFree(table);
 			    break;
 			  case PS_META_META:
-			    // This is a menu
-			    psMetadata *options = dataItem->data.V; // Options with concept values as keys
-			    bool mdok = false; // Success of MD lookup
-			    psString concept = psMetadataLookupStr(&mdok, recipe, "NONLIN.SOURCE");
-			    if (! mdok || ! concept) {
-				psLogMsg("phase2", PS_LOG_WARN, "Non-linearity correction desired, but "
-					 "unable to find NONLIN.SOURCE in recipe --- ignored.\n");
-			    } else {
-				psMetadataItem *conceptValueItem = pmCellGetConcept(inputCell, concept);
-				if (! conceptValueItem) {
-				    psLogMsg("phase2", PS_LOG_WARN, "Unable to find value of concept %s "
-					     "for non-linearity correction --- ignored.\n", concept);
-				} else if (conceptValueItem->type != PS_META_STR) {
-				    psLogMsg("phase2", PS_LOG_WARN, "Type for concept %s isn't STRING, as"
-					     " expected for non-linearity correction --- ignored.\n",
-					     concept);
+			    {
+				// This is a menu
+				psMetadata *options = dataItem->data.V; // Options with concept values as keys
+				bool mdok = false; // Success of MD lookup
+				psString concept = psMetadataLookupString(&mdok, recipe, "NONLIN.SOURCE");
+				if (! mdok || ! concept) {
+				    psLogMsg("phase2", PS_LOG_WARN, "Non-linearity correction desired, but "
+					     "unable to find NONLIN.SOURCE in recipe --- ignored.\n");
 				} else {
-				    psString conceptValue = conceptValueItem->data.V;
-				    // Get the value of the concept
-				    psMetadataItem *optionItem = psMetadataLookup(options, conceptValue);
-				    if (!optionItem) {
-					psLogMsg("phase2", PS_LOG_WARN, "Unable to find %s in NONLIN.DATA"
-						 " --- ignored.\n", conceptValue);
-				    } else if (optionItem->type == PS_META_VECTOR) {
-					// These are the polynomial coefficients
-					psVector *coeff = optionItem->data.V; // The coefficient vector
-					if (coeff->type.type != PS_TYPE_F64) {
-					    psVector *temp = psVectorCopy(NULL, coeff, PS_TYPE_F64);
+				    psMetadataItem *conceptValueItem = pmCellGetConcept(inputCell, concept);
+				    if (! conceptValueItem) {
+					psLogMsg("phase2", PS_LOG_WARN, "Unable to find value of concept %s "
+						 "for non-linearity correction --- ignored.\n", concept);
+				    } else if (conceptValueItem->type != PS_META_STR) {
+					psLogMsg("phase2", PS_LOG_WARN, "Type for concept %s isn't STRING, as"
+						 " expected for non-linearity correction --- ignored.\n",
+						 concept);
+				    } else {
+					psString conceptValue = conceptValueItem->data.V;
+					// Get the value of the concept
+					psMetadataItem *optionItem = psMetadataLookup(options, conceptValue);
+					if (!optionItem) {
+					    psLogMsg("phase2", PS_LOG_WARN, "Unable to find %s in NONLIN.DATA"
+						     " --- ignored.\n", conceptValue);
+					} else if (optionItem->type == PS_META_VEC) {
+					    // These are the polynomial coefficients
+					    psVector *coeff = optionItem->data.V; // The coefficient vector
+					    if (coeff->type.type != PS_TYPE_F64) {
+						psVector *temp = psVectorCopy(NULL, coeff, PS_TYPE_F64);
+						psFree(coeff);
+						coeff = temp;
+					    }
+					    // Polynomial correction
+					    psPolynomial1D *correction = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD,
+											     coeff->n + 1);
+					    for (int i = 0; i < coeff->n; i++) {
+						correction->coeff[i] = coeff->data.F64[i];
+					    }
+					    (void)pmNonLinearityPolynomial(inputReadout, correction);
 					    psFree(coeff);
-					    coeff = temp;
+					    psFree(correction);
+					} else if (optionItem->type == PS_META_STR) {
+					    // This is a filename
+					    psString tableName = optionItem->data.V; // The filename
+					    psLookupTable *table = psLookupTableAlloc(tableName, "%f %f", 0);
+					    int numLines = 0; // Number of lines read from table
+					    if ((numLines = psLookupTableRead(table)) <= 0) {
+						psErrorStackPrint(stderr, "Unable to read non-linearity "
+								  "correction file %s --- ignored\n",
+								  tableName);
+					    } else {
+#ifdef PRODUCTION
+						(void)pmNonLinearityLookup(inputReadout, table);
+#else
+						printf("XXX: Non-linearity correction from lookup table not "
+						       "yet implemented.\n");
+#endif
+					    }
+					    psFree(table);
+					} else {
+					    psLogMsg("phase2", PS_LOG_WARN, "Non-linearity correction "
+						     "desired but unable to interpret NONLIN.DATA for %s"
+						     " --- ignored\n", conceptValue);
 					}
-					// Polynomial correction
-					psPolynomial1D *correction = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD,
-											 coeff->n + 1);
-					for (int i = 0; i < coeff->n; i++) {
-					    correction->coeff[i] = coeff->data.F64[i];
-					}
-					(void)pmNonLinearityPolynomial(inputReadout, correction);
-					psFree(coeffCopy);
-					psFree(coeff);
-					psFree(correction);
-				    } else if (optionItem->type == PS_META_STR) {
-					// This is a filename
-					psString tableName = optionItem->data.V; // The filename
-					psLookupTable *table = psLookupTableAlloc(tableName, "%f %f", 0);
-					if ((int numLines = psLookupTableRead(table)) <= 0) {
-					    psErrorStackPrint(stderr, "Unable to read non-linearity "
-							      "correction file %s --- ignored\n", tableName);
-					} else {
-					    (void)pmNonLinearityLookup(inputReadout, table);
-					}
-					psFree(table);
-				    } else {
-					psLogMsg("phase2", PS_LOG_WARN, "Non-linearity correction "
-						 "desired but unable to interpret NONLIN.DATA for %s"
-						 " --- ignored\n", conceptValue);
 				    }
 				}
@@ -553,7 +613,9 @@
 		// it's not in there yet.  Once it is, we can get rid of "pedestal".
 
+#ifdef PRODUCTION
 		psImage *pedestal = NULL;	// Pedestal image (bias + scaled dark)
 		if (doDark) {
-		    float inputTime = pmReadoutGetDarkTime(inputReadout); // Dark time for input image
+		    // Dark time for input image
+		    float inputTime = psMetadataLookupF32(NULL, inputCell->concepts, "CELL.DARKTIME");
 		    if (inputTime <= 0) {
 			psErrorStackPrint(stderr, "DARKTIME for input image (%f) is non-positive.\n",
@@ -562,6 +624,6 @@
 		    }
 
-		    pedestal = psBinaryOp(NULL, darkImage, "*",
-					  psScalarAlloc(inputTime * darkTime, PS_TYPE_F32));
+		    pedestal = (psImage*)psBinaryOp(NULL, darkImage, "*",
+						    psScalarAlloc(inputTime * darkTime, PS_TYPE_F32));
 		}
 		if (doBias) {
@@ -569,5 +631,5 @@
 			if (biasImage->numRows != darkImage->numRows ||
 			    biasImage->numCols != darkImage->numCols) {
-			    psError("phase2", true, "Bias and dark images have different dimensions: "
+			    psError(PS_ERR_IO, true, "Bias and dark images have different dimensions: "
 				    "%dx%d vs %dx%d\n", biasImage->numCols, biasImage->numRows,
 				    darkImage->numCols, darkImage->numRows);
@@ -579,8 +641,8 @@
 		    }
 		}
-
+#endif
 		if (overscanMode == PM_OVERSCAN_ROWS || overscanMode == PM_OVERSCAN_COLUMNS) {
 		    // Need to get the read direction
-		    int readdir = pmCellGetReaddir(inputCell); // Read direction
+		    int readdir = psMetadataLookupS32(NULL, inputCell->concepts, "CELL.READDIR");
 		    if (readdir == 1) {
 			overscanMode = PM_OVERSCAN_ROWS;
@@ -594,39 +656,53 @@
 		}
 
-		void *overscanResult = NULL; // Result of overscan fit
-		(void)pmSubtractBias(inputImage, overscanResult, inputBias, overscanMode, overscanStat,
-				     overscanBin, overscanFit, pedestal);
-
-		// Output overscan fit results, if required
-		if (doOverscan) {
-		    switch (overscanFit) {
-		      case PM_FIT_POLYNOMIAL:
-			psPolynomial1D *poly = (psPolynomial1D*)overscanResult;	// The polynomial
-			psString coeffs = NULL;	// String containing the coefficients
-			for (int i = 0; i < poly->n; i++) {
-			    psStringAppend(&coeffs, "%.2f ", poly->coeffs[i]);
+		printf("mode: %d\n", overscanMode);
+
+		if (doBias || doOverscan || doDark) {
+		    void *overscanResult = NULL; // Result of overscan fit
+#ifdef PRODUCTION
+		    (void)pmSubtractBias(inputReadout, overscanResult, inputOverscans, overscanMode,
+					 overscanStats, overscanBins, overscanFit, pedestal);
+#else
+		    (void)pmSubtractBias(inputReadout, overscanResult, inputOverscans, overscanMode,
+					 overscanStats, overscanBins, overscanFit, biasReadout);
+#endif
+		    
+		    // Output overscan fit results, if required
+		    if (doOverscan) {
+			switch (overscanFit) {
+			  case PM_FIT_POLYNOMIAL:
+			    {
+				psPolynomial1D *poly = (psPolynomial1D*)overscanResult;	// The polynomial
+				psString coeffs = NULL;	// String containing the coefficients
+				for (int i = 0; i < poly->n; i++) {
+				    psStringAppend(&coeffs, "%.2f ", poly->coeff[i]);
+				}
+				psLogMsg("phase2", PS_LOG_INFO, "Overscan polynomial coefficients:\n%s\n",
+					 coeffs);
+				psFree(coeffs);
+			    }
+			    break;
+			  case PM_FIT_SPLINE:
+			    {
+				psSpline1D *spline = (psSpline1D*)overscanResult; // The spline
+				psString coeffs = NULL;	// String containing the coefficients
+				for (int i = 0; i < spline->n; i++) {
+				    psPolynomial1D *poly = spline->spline[i]; // i-th polynomial
+				    psStringAppend(&coeffs, "%d: ", i);
+				    for (int j = 0; j < poly->n; j++) {
+					psStringAppend(&coeffs, "%.2f ", poly->coeff[i]);
+				    }
+				    psStringAppend(&coeffs, "\n");
+				}
+				psLogMsg("phase2", PS_LOG_INFO, "Overscan spline coefficients:\n%s\n",
+					 coeffs);
+				psFree(coeffs);
+			    }
+			    break;
+			  case PM_FIT_NONE:
+			    break;
+			  default:
+			    psAbort(__func__, "Should never get here!!!\n");
 			}
-			psLogMsg("phase2", PS_LOG_INFO, "Overscan polynomial coefficients:\n%s\n",
-				 coeffs);
-			psFree(coeffs);
-			break;
-		      case PM_FIT_SPLINE:
-			psSpline1D *spline = (psSpline1D*)overscanResult; // The spline
-			psString coeffs = NULL;	// String containing the coefficients
-			for (int i = 0; i < spline->n; i++) {
-			    psPolynomial1D *poly = spline->spline[i]; // i-th polynomial
-			    psStringAppend(&coeffs, "%d: ", i);
-			    for (int j = 0; j < poly->n; j++) {
-				psStringAppend(&coeffs, "%.2f ", poly->coeffs[i]);
-			    }
-			    psStringAppend(&coeffs, "\n");
-			}
-			psLogMsg("phase2", PS_LOG_INFO, "Overscan spline coefficients:\n%s\n", coeffs);
-			psFree(coeffs);
-			break;
-		      case PM_FIT_NONE:
-			break;
-		      default:
-			psAbort("Should never get here!!!\n");
 		    }
 		}
@@ -634,5 +710,5 @@
 		// Flat-field correction
 		if (doFlat) {
-		    (void)pmFlatField(inputReadout, maskReadout, flatReadout);
+		    (void)pmFlatField(inputReadout, flatReadout);
 		}
 
@@ -659,16 +735,15 @@
 
     // Write the output
-    pmFPAWrite(outputFile, input);
-    pmFPAWriteMask(outputMaskFile, input);
+    pmFPAWrite(outputFile, input, database);
+//    pmFPAWriteMask(outputMaskFile, input);
 #ifdef PRODUCTION
     psFitsClose(outputFile);
-    psFitsClose(outputMaskFile);
+//    psFitsClose(outputMaskFile);
 #else
     psFree(outputFile);
-    psFree(outputMaskFile);
-#endif
-
-#endif
-
+//    psFree(outputMaskFile);
+#endif
+
+    psFree(arguments);
     psFree(site);
     psFree(header);
@@ -681,5 +756,4 @@
     psFree(flat);
     psFree(overscanStats);
-    psFree(inputFile);
 
 #if 1
Index: /trunk/archive/scripts/src/phase2/papStuff.c
===================================================================
--- /trunk/archive/scripts/src/phase2/papStuff.c	(revision 5103)
+++ /trunk/archive/scripts/src/phase2/papStuff.c	(revision 5104)
@@ -6,4 +6,15 @@
 
 #include "papStuff.h"
+
+
+static void memPrint(const psPtr ptr)
+{
+    psMemBlock *mb = ((psMemBlock*)ptr) - 1;
+    printf("Memory block %lld (%lld):\n"
+	   "\tFile %s, line %d, size %d\n"
+	   "\tPosts: %x %x %x\n",
+	   mb->id, mb->refCounter, mb->file, mb->lineno, mb->userMemorySize, mb->startblock, mb->endblock,
+	   *(void**)((int8_t *)(mb + 1) + mb->userMemorySize));
+}
 
 // Split string on given characters
@@ -30,4 +41,5 @@
 		(void)psListAdd(values, PS_LIST_TAIL, word);
 		start = i + 1;
+		psFree(word);
 	    }
 	}
@@ -37,4 +49,5 @@
 	psString word = psStringNCopy(&string[start], length - start);
 	(void)psListAdd(values, PS_LIST_TAIL, word);
+	psFree(word);
     }
 
Index: /trunk/archive/scripts/src/phase2/pmConfig.c
===================================================================
--- /trunk/archive/scripts/src/phase2/pmConfig.c	(revision 5103)
+++ /trunk/archive/scripts/src/phase2/pmConfig.c	(revision 5104)
@@ -133,5 +133,5 @@
 	}
 	psTrace(__func__, 7, "Setting log destination to STDOUT.\n");
-	psLogSetDestination(0);
+	psLogSetDestination(PS_LOG_TO_STDOUT);
     }
 
@@ -147,5 +147,5 @@
 	    }
 	    psTrace(__func__, 7, "Setting trace level for %s to %d\n", traceItem->name, traceItem->data.S32);
-	    (void)psTraceSetLevel(traceItem->name, traceItem->data.S32);
+	    psTraceSetLevel(traceItem->name, traceItem->data.S32);
 	}
 	psFree(traceIter);
Index: /trunk/archive/scripts/src/phase2/pmFPA.c
===================================================================
--- /trunk/archive/scripts/src/phase2/pmFPA.c	(revision 5103)
+++ /trunk/archive/scripts/src/phase2/pmFPA.c	(revision 5104)
@@ -1,4 +1,5 @@
 #include <stdio.h>
 #include <string.h>
+#include <assert.h>
 #include "pslib.h"
 #include "psAdditionals.h"
@@ -6,15 +7,31 @@
 #include "pmFPA.h"
 
-
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 // Allocators
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
-pmFPA *pmFPAAlloc(const psMetadata *camera, // Camera configuration
-		  psDB *db		// Database
+p_pmHDU *p_pmHDUAlloc(const char *extname)
+{
+    p_pmHDU *hdu = psAlloc(sizeof(p_pmHDU));
+    psMemSetDeallocator(hdu, (psFreeFunc)p_pmHDUFree);
+
+    hdu->extname = extname;
+    hdu->pixels = NULL;
+    hdu->header = NULL;
+
+    return hdu;
+}
+
+void p_pmHDUFree(p_pmHDU *hdu)
+{
+    psFree(hdu->pixels);
+    psFree(hdu->header);
+}
+
+pmFPA *pmFPAAlloc(const psMetadata *camera // Camera configuration
     )
 {
     pmFPA *fpa = psAlloc(sizeof(pmFPA));// The FPA
-    psMemSetDeallocator(fpa, (psFreeFcn)p_pmFPAFree);
+    psMemSetDeallocator(fpa, (psFreeFunc)p_pmFPAFree);
 
     // Fill in the components
@@ -23,12 +40,9 @@
     fpa->projection = NULL;
 
-    fpa->values = psMetadataAlloc();
-//    fpa->camera = psMemIncrRefCounter((psPtr)camera);
-    fpa->db = db;
+    fpa->concepts = psMetadataAlloc();
+    fpa->camera = psMemIncrRefCounter((psPtr)camera);
     fpa->chips = psArrayAlloc(0);
 
-    fpa->extname = NULL;
-    fpa->pixels = NULL;
-    fpa->header = NULL;
+    fpa->private = NULL;
 
     return fpa;
@@ -41,11 +55,9 @@
     psFree(fpa->projection);
 
-    psFree(fpa->values);
+    psFree(fpa->concepts);
     psFree((psPtr)fpa->camera);
-    psFree(fpa->db);
     psFree(fpa->chips);
 
-    psFree(fpa->pixels);
-    psFree(fpa->header);
+    psFree(fpa->private);
 }
 
@@ -55,5 +67,5 @@
 {
     pmChip *chip = psAlloc(sizeof(pmChip)); // The chip
-    psMemSetDeallocator(chip, (psFreeFcn)p_pmChipFree);
+    psMemSetDeallocator(chip, (psFreeFunc)p_pmChipFree);
 
     // Push onto the array of chips
@@ -67,13 +79,14 @@
     chip->fromFPA = NULL;
 
-    chip->values = psMetadataAlloc();
-//    chip->parent = psMemIncrRefCounter(fpa);
+    chip->concepts = psMetadataAlloc();
     chip->cells = psArrayAlloc(0);
-
-    chip->extname = NULL;
-    chip->pixels = NULL;
-    chip->header = NULL;
-
-    psMetadataAddStr(chip->values, PS_LIST_HEAD, "CHIP.NAME", "Chip name added at pmChipAlloc", name);
+    chip->parent = fpa;			// We don't increment the reference counter on this --- it's a
+					// "hidden" link.  If we increment the reference counter, we get stuck
+					// in a circle.
+    chip->valid = true;    
+
+    chip->private = NULL;
+
+    psMetadataAddStr(chip->concepts, PS_LIST_HEAD, "CHIP.NAME", 0, "Chip name added at pmChipAlloc", name);
 
     return chip;
@@ -85,19 +98,20 @@
     psFree(chip->fromFPA);
 
-    psFree(chip->values);
+    psFree(chip->concepts);
     psFree(chip->cells);
-//    psFree(chip->parent);
-
-    psFree(chip->pixels);
-    psFree(chip->header);
+
+    psFree(chip->private);
+
+    // We don't free the parent member, since that would generate a circular call.  We don't increment the
+    // reference counter when we add it, anyway, so that's OK.
 }
 
 pmCell *pmCellAlloc(pmChip *chip,	// Chip to which the cell belongs
-		    psMetadata *values,	// Initial values for concepts
+		    psMetadata *cameraData, // Camera data
 		    psString name	// Name of cell
     )
 {
     pmCell *cell = psAlloc(sizeof(pmCell)); // The cell
-    psMemSetDeallocator(cell, (psFreeFcn)p_pmCellFree);
+    psMemSetDeallocator(cell, (psFreeFunc)p_pmCellFree);
 
     // Push onto the array of chips
@@ -114,13 +128,15 @@
     cell->toSky = NULL;
 
-    cell->values = psMemIncrRefCounter(values);
+    cell->concepts = psMetadataAlloc();
+    psMetadataAddStr(cell->concepts, PS_LIST_HEAD, "CELL.NAME", 0, "Cell name added at pmCellAlloc", name);
+    cell->camera = psMemIncrRefCounter(cameraData);
+
     cell->readouts = psArrayAlloc(0);
-//    cell->parent = psMemIncrRefCounter(chip);
-
-    cell->extname = NULL;
-    cell->pixels = NULL;
-    cell->header = NULL;
-
-    psMetadataAddStr(cell->values, PS_LIST_HEAD, "CELL.NAME", "Cell name added at pmCellAlloc", name);
+    cell->parent = chip;		// We don't increment the reference counter on this --- it's a
+					// "hidden" link.  If we increment the reference counter, we get stuck
+					// in a circle.
+    cell->valid = true;
+
+    cell->private = NULL;
 
     return cell;
@@ -135,14 +151,15 @@
     psFree(cell->toSky);
 
-    psFree(cell->values);
+    psFree(cell->concepts);
+    psFree(cell->camera);
     psFree(cell->readouts);
-//    psFree(cell->parent);
-
-    psFree(cell->pixels);
-    psFree(cell->header);
+
+    psFree(cell->private);
+
+    // We don't free the parent member, since that would generate a circular call.  We don't increment the
+    // reference counter when we add it, anyway, so that's OK.
 }
 
 pmReadout *pmReadoutAlloc(pmCell *cell, // Cell to which the readout belongs
-			  int readoutNum, // Number of the readout
 			  psImage *image, // The pixels
 			  psList *overscans, // The overscan images
@@ -151,17 +168,13 @@
 {
     pmReadout *readout = psAlloc(sizeof(pmReadout));
-    psMemSetDeallocator(readout, (psFreeFcn)p_pmReadoutFree);
-    psArray *readouts = cell->readouts;
-    if (readoutNum >= readouts->nalloc) {
-	readouts = psArrayRealloc(readouts, readoutNum);
-	cell->readouts = readouts;
-    }
-    readouts->data[readoutNum] = readout;
+    psMemSetDeallocator(readout, (psFreeFunc)p_pmReadoutFree);
+    cell->readouts = psArrayAdd(cell->readouts, 0, readout);
     
     // Set the components
-    readout->image = image;
-    readout->overscans = overscans;
+    readout->image = psMemIncrRefCounter(image);
+    readout->mask = NULL;
+    readout->overscans = psMemIncrRefCounter(overscans);
     
-    readout->values = psMetadataAlloc();
+    //readout->concepts = psMetadataAlloc();
     
     *(int*)&readout->col0 = col0;
@@ -178,5 +191,70 @@
 {
     psFree(readout->image);
+    psFree(readout->mask); 
     psFree(readout->overscans);
-    psFree(readout->values);
-}
+    //psFree(readout->concepts);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Select and Exclude chips
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool pmFPASelectChip(pmFPA *fpa, int chipNum)
+{
+    assert(fpa);
+
+    if (chipNum < 0 || chipNum > fpa->chips->n) {
+	return false;
+    }
+    psArray *chips = fpa->chips;	// Component chips
+    for (int i = 0; i < chips->n; i++) {
+	pmChip *chip = chips->data[i];	// The chip of interest
+	if (i == chipNum) {
+	    psTrace(__func__, 5, "Marking chip %d valid.\n", i);
+	    chip->valid = true;
+	    psArray *cells = chip->cells; // Component cells
+	    for (int j = 0; j < cells->n; j++) {
+		pmCell *cell = cells->data[j]; // Cell of interest
+		cell->valid = true;
+	    }
+	} else {
+	    psTrace(__func__, 5, "Marking chip %d invalid.\n", i);
+	    chip->valid = false;
+	    psArray *cells = chip->cells; // Component cells
+	    for (int j = 0; j < cells->n; j++) {
+		pmCell *cell = cells->data[j]; // Cell of interest
+		cell->valid = false;
+	    }
+	}
+    }
+    return true;
+}
+
+int pmFPAExcludeChip(pmFPA *fpa, int chipNum)
+{
+    assert(fpa);
+
+    if (chipNum < 0 || chipNum > fpa->chips->n) {
+	psLogMsg(__func__, PS_LOG_WARN, "Invalid chip number: %d\n", chipNum);
+    }
+    int numValid = 0;			// Number of valid chips
+    psArray *chips = fpa->chips;	// Component chips
+    for (int i = 0; i < chips->n; i++) {
+	pmChip *chip = chips->data[i];	// The chip of interest
+	if (i == chipNum) {
+	    psTrace(__func__, 5, "Marking chip %d invalid.\n", i);
+	    chip->valid = false;
+	    psArray *cells = chip->cells; // Component cells
+	    for (int j = 0; j < cells->n; j++) {
+		pmCell *cell = cells->data[j]; // Cell of interest
+		cell->valid = false;
+	    }
+	} else if (chip->valid) {
+	    numValid++;
+	}
+    }
+
+    psTrace(__func__, 5, "%d valid chips in fpa.\n", numValid);
+    return numValid;
+}
Index: /trunk/archive/scripts/src/phase2/pmFPA.h
===================================================================
--- /trunk/archive/scripts/src/phase2/pmFPA.h	(revision 5103)
+++ /trunk/archive/scripts/src/phase2/pmFPA.h	(revision 5104)
@@ -10,4 +10,10 @@
 
 typedef struct {
+    const char *extname;		// Extension name, if it corresponds to this level
+    psArray *pixels;			// The pixel data, if it corresponds to this level
+    psMetadata *header;			// The FITS header, if it corresponds to this level
+} p_pmHDU;
+
+typedef struct {
     // Astrometric transformations
     psPlaneDistort* fromTangentPlane;	// Transformation from tangent plane to focal plane
@@ -15,12 +21,9 @@
     psProjection *projection;		// Projection from tangent plane to sky
     // Information
-    psMetadata *values;			// Important values (cached)
-    const psMetadata *camera;		// Camera configuration
-    psDB *db;				// Database
+    psMetadata *camera;			// Camera configuration
+    psMetadata *concepts;		// Values for concepts
+    psMetadata *phu;			// Primary Header
     psArray *chips;			// The chips
-    // FITS data
-    const char *extname;		// Extension name, if it corresponds to this level
-    psArray *pixels;			// The pixel data, if it corresponds to this level
-    psMetadata *header;			// The FITS header, if it corresponds to this level
+    p_pmHDU *private;			// FITS data
 } pmFPA;
 
@@ -33,11 +36,9 @@
     psPlaneTransform* fromFPA;		// Transformation from FPA to chip coordinates
     // Information
-    psMetadata *values;			// Important values (cached)
+    psMetadata *concepts;		// Values for concepts
     psArray *cells;			// The cells (referred to by name)
     pmFPA *parent;			// Parent FPA
-    // FITS data
-    const char *extname;		// Extension name, if it corresponds to this level
-    psArray *pixels;			// The pixel data, if it corresponds to this level
-    psMetadata *header;			// The FITS header, if it corresponds to this level
+    bool valid;				// Valid for reading in?
+    p_pmHDU *private;			// FITS data
 } pmChip;
 
@@ -53,11 +54,10 @@
     psPlaneTransform* toSky;		// Transformations from cell to tangent plane coordinates
     // Information
-    psMetadata *values;			// Important values (cached)
+    psMetadata *concepts;		// Values for concepts
+    psMetadata *camera;			// Camera information
     psArray *readouts;			// The readouts (referred to by number)
     pmChip *parent;			// Parent chip
-    // FITS data
-    const char *extname;		// Extension name, if it corresponds to this level
-    psArray *pixels;			// The pixel data, if it corresponds to this level
-    psMetadata *header;			// The FITS header, if it corresponds to this level
+    bool valid;				// Valid for operating on?
+    p_pmHDU *private;			// FITS data
 } pmCell;
 
@@ -73,11 +73,13 @@
     // Information
     psImage *image;			// The pixels
-    psList *overscans;			// Array of subimages containing the overscan region(s)
-    psMetadata *values;			// readout-level metadata
+    psImage *mask;			// Mask image
+    psList *overscans;			// List of subimages containing the overscan regions
+    psMetadata *concepts;		// Concepts for readouts
 } pmReadout;
 
 // Allocators and deallocators
-pmFPA *pmFPAAlloc(const psMetadata *camera, // Camera configuration
-		  psDB *db		// Database
+p_pmHDU *p_pmHDUAlloc(const char *extname);
+void p_pmHDUFree(p_pmHDU *hdu);
+pmFPA *pmFPAAlloc(const psMetadata *camera // Camera configuration
     );
 void p_pmFPAFree(pmFPA *fpa);
@@ -89,5 +91,5 @@
 
 pmCell *pmCellAlloc(pmChip *chip,	// Chip to which the cell belongs
-		    psMetadata *values,	// Initial values for concepts
+		    psMetadata *cameraData, // Camera data
 		    psString name	// Name of cell
     );
@@ -95,5 +97,4 @@
 
 pmReadout *pmReadoutAlloc(pmCell *cell,	// Cell to which the readout belongs
-			  int readoutNum, // Number of the readout
 			  psImage *image, // The pixels
 			  psList *overscans, // The overscan images
@@ -102,3 +103,7 @@
 void p_pmReadoutFree(pmReadout *readout);
 
+// Select and exclude chips
+bool pmFPASelectChip(pmFPA *fpa, int chipNum);
+int pmFPAExcludeChip(pmFPA *fpa, int chipNum);
+
 #endif
Index: /trunk/archive/scripts/src/phase2/pmFPAConstruct.c
===================================================================
--- /trunk/archive/scripts/src/phase2/pmFPAConstruct.c	(revision 5103)
+++ /trunk/archive/scripts/src/phase2/pmFPAConstruct.c	(revision 5104)
@@ -30,5 +30,27 @@
 	psError(PS_ERR_IO, false, "Unable to find specs for cell %s: ignored\n", cellName);
     }
+
+#if 0
+    // Need to create a new instance, so that each cell can work with its own
+    psMetadata *copy = psMetadataAlloc();
+    psMetadataIterator *iter = psMetadataIteratorAlloc(cellData, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *item = NULL;	// Item from iteration
+    while (item = psMetadataGetAndIncrement(iter)) {
+	if (item->type == PS_META_MULTI || item->type == PS_META_META) {
+	    psLogMsg(__func__, PS_LOG_WARN, "PS_META_MULTI and PS_META_META are not supported in a cell "
+		     "definition --- %s ignored.\n", item->name);
+	    continue;
+	}
+	if (! psMetadataAdd(copy, PS_LIST_TAIL, item->name, item->type, item->comment, item->data.V)) {
+	    psAbort(__func__, "Should never reach here!\n");
+	}
+    }
+    psFree(iter);
+
+    return copy;
+#else
     return cellData;
+#endif
+
 }
 
@@ -38,9 +60,8 @@
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
-pmFPA *pmFPAConstruct(const psMetadata *camera, // The camera configuration
-		      psDB *db		// Database handle
+pmFPA *pmFPAConstruct(const psMetadata *camera // The camera configuration
     )
 {
-    pmFPA *fpa = pmFPAAlloc(camera, db); // The FPA to fill out
+    pmFPA *fpa = pmFPAAlloc(camera); // The FPA to fill out
     bool mdStatus = true;		// Status from metadata lookups
     const char *phuType = psMetadataLookupString(&mdStatus, camera, "PHU"); // What is the PHU?
@@ -67,5 +88,5 @@
 		psString extName = contentItem->name; // The name of the extension
 		pmChip *chip = pmChipAlloc(fpa, extName); // The chip
-		chip->extname = extName;// Mark chip to receive FITS data
+		chip->private = p_pmHDUAlloc(extName); // Prepare chip to receive FITS data
 		if (contentItem->type != PS_META_STR) {
 		    psLogMsg(__func__, PS_LOG_WARN, "Type of content item (%x) is not string: ignored\n",
@@ -83,4 +104,5 @@
 		    psMetadata *cellData = getCellData(camera, cellName);
 		    pmCell *cell = pmCellAlloc(chip, cellData, cellName); // The cell
+//		    psFree(cellData);
 		}
 		psFree(cellNamesIter);
@@ -102,5 +124,5 @@
 		const char *content = contentItem->data.V; // The content of the extension
 		psList *contents = papSplit(content, ": "); // Split the name from the type
-		if (contents->size != 2) {
+		if (contents->n != 2) {
 		    psLogMsg(__func__, PS_LOG_WARN, "Unable to read contents of %s: ignored.\n", extName);
 		} else {
@@ -123,10 +145,9 @@
 		    psMetadata *cellData = getCellData(camera, cellType);
 		    pmCell *cell = pmCellAlloc(chip, cellData, extName); // The cell
-		    cell->extname = extName; // Mark cell to receive FITS data
+//		    psFree(cellData);
+		    cell->private = p_pmHDUAlloc(extName); // Prepare cell to receive FITS data
 
 		    psFree(chip);
 		    psFree(cell);
-		    psFree(chipName);
-		    psFree(cellType);
 		}
 		psFree(contents);
@@ -136,5 +157,5 @@
 	} else if (strcasecmp(extType, "NONE") == 0) {
 	    // No extensions; Content contains metadata, each entry is a chip with its component cells
-	    fpa->extname = "PHU";
+	    fpa->private = p_pmHDUAlloc("PHU");	// Prepare FPA to receive FITS data
 	    while (contentItem = psMetadataGetAndIncrement(contentsIter)) {
 		psString chipName = contentItem->name; // The name of the chip
@@ -155,4 +176,5 @@
 		    psMetadata *cellData = getCellData(camera, cellName);
 		    pmCell *cell = pmCellAlloc(chip, cellData, cellName); // The cell
+//		    psFree(cellData);
 		}
 		psFree(cellNamesIter);
@@ -174,5 +196,5 @@
 	if (strcasecmp(extType, "NONE") == 0) {
 	    // There are no extensions --- only the PHU
-	    chip->extname = "PHU";
+	    chip->private = p_pmHDUAlloc("PHU");
 
 	    const char *contents = psMetadataLookupString(&mdStatus, camera, "CONTENTS");
@@ -188,4 +210,5 @@
 		psMetadata *cellData = getCellData(camera, cellName);
 		pmCell *cell = pmCellAlloc(chip, cellData, cellName); // The cell
+//		psFree(cellData);
 	    }
 	    psFree(cellIter);
@@ -223,5 +246,6 @@
 		psMetadata *cellData = getCellData(camera, cellType);
 		pmCell *cell = pmCellAlloc(chip, cellData, extName); // The cell
-		cell->extname = extName; // Mark cell to receive FITS data
+//		psFree(cellData);
+		cell->private = p_pmHDUAlloc(extName); // Prepare cell to receive FITS data
 	    } // Iterating through contents
 	    psFree(contentsIter);
@@ -249,8 +273,8 @@
 {
     psTrace(__func__, 0, "FPA:\n");
-    if (fpa->extname) {
-	psTrace(__func__, 1, "---> FPA is extension %s.\n", fpa->extname);
-	if (! fpa->pixels) {
-	    psTrace(__func__, 1, "---> NO PIXELS for extension %s\n", fpa->extname);
+    if (fpa->private) {
+	psTrace(__func__, 1, "---> FPA is extension %s.\n", fpa->private->extname);
+	if (! fpa->private->pixels) {
+	    psTrace(__func__, 1, "---> NO PIXELS for extension %s\n", fpa->private->extname);
 	}
     }
@@ -261,8 +285,8 @@
 	psTrace(__func__, 1, "Chip: %d\n", i);
 	pmChip *chip = chips->data[i]; // The chip
-	if (chip->extname) {
-	    psTrace(__func__, 2, "---> Chip is extension %s.\n", chip->extname);
-	    if (! chip->pixels) {
-		psTrace(__func__, 2, "---> NO PIXELS for extension %s\n", chip->extname);
+	if (chip->private) {
+	    psTrace(__func__, 2, "---> Chip is extension %s.\n", chip->private->extname);
+	    if (! chip->private->pixels) {
+		psTrace(__func__, 2, "---> NO PIXELS for extension %s\n", chip->private->extname);
 	    }
 	}
@@ -272,11 +296,11 @@
 	    psTrace(__func__, 2, "Cell: %d\n", j);
 	    pmCell *cell = cells->data[j]; // The cell
-	    if (cell->extname) {
-		psTrace(__func__, 3, "---> Cell is extension %s.\n", cell->extname);
-		if (! cell->pixels) {
-		    psTrace(__func__, 3, "---> NO PIXELS for extension %s\n", cell->extname);
-		}
-	    }
-	    psMetadataPrint(cell->values, 3);
+	    if (cell->private) {
+		psTrace(__func__, 3, "---> Cell is extension %s.\n", cell->private->extname);
+		if (! cell->private->pixels) {
+		    psTrace(__func__, 3, "---> NO PIXELS for extension %s\n", cell->private->extname);
+		}
+	    }
+	    psMetadataPrint(cell->concepts, 3);
 	    psTrace(__func__, 3, "Readouts:\n");
 	    psArray *readouts = cell->readouts;	// Array of readouts
Index: /trunk/archive/scripts/src/phase2/pmFPAConstruct.h
===================================================================
--- /trunk/archive/scripts/src/phase2/pmFPAConstruct.h	(revision 5103)
+++ /trunk/archive/scripts/src/phase2/pmFPAConstruct.h	(revision 5104)
@@ -6,6 +6,5 @@
 
 // Read the contents of a FITS file (format specified by the camera configuration) into memory
-pmFPA *pmFPAConstruct(const psMetadata *camera, // The camera configuration
-		      psDB *db		// Database handle
+pmFPA *pmFPAConstruct(const psMetadata *camera // The camera configuration
     );
 
Index: /trunk/archive/scripts/src/phase2/psAdditionals.c
===================================================================
--- /trunk/archive/scripts/src/phase2/psAdditionals.c	(revision 5103)
+++ /trunk/archive/scripts/src/phase2/psAdditionals.c	(revision 5104)
@@ -103,4 +103,5 @@
 	    break;
 	  default:
+	    printf("\n");
 	    psError(PS_ERR_IO, false, "Non-printable metadata type: %x\n", item->type);
 	}
@@ -262,5 +263,5 @@
 		// Go through the MULTI
 		psList *multi = argItem->data.V; // The list of MULTI psMetadataItems
-		if (*argc < i + multi->size) {
+		if (*argc < i + multi->n) {
 		    psError(PS_ERR_IO, true, "Not enough arguments for %s.\n", argItem->name);
 		    // Remove the arguments --- they will be ignored
Index: /trunk/archive/scripts/src/phase2/psAdditionals.h
===================================================================
--- /trunk/archive/scripts/src/phase2/psAdditionals.h	(revision 5103)
+++ /trunk/archive/scripts/src/phase2/psAdditionals.h	(revision 5104)
@@ -3,6 +3,4 @@
 
 #include "pslib.h"
-
-typedef char* psString;
 
 // Get a value from the metadata that we believe should be metadata.
