Index: /branches/eam_branches/20091201/psphot/doc/stack.txt
===================================================================
--- /branches/eam_branches/20091201/psphot/doc/stack.txt	(revision 26541)
+++ /branches/eam_branches/20091201/psphot/doc/stack.txt	(revision 26542)
@@ -1,2 +1,44 @@
+
+20100107 : updates for stack processing
+
+  I am building a new version of psphot that operates on a set of
+  images (eg, multi-filter or multi-epoch).  The assumptions are:
+
+  * each input image represents the same sky pixels : they are warped to a common frame.
+  * each image has been previously processed, with the background
+    subtracted (and the psf model determined?)
+  * all planes are / can be loaded into memory at once (otherwise I
+    need to add a lot of I/O layers)
+
+  psphot currently expects the pmFPAfile of interest to be available in
+  config->files with the name PSPHOT.INPUT.  To update the code for
+  stacks, I am extending this concept with the pmFPAfileSelectSingle
+  API: config->files may contain multiple PSPHOT.INPUT entries, and
+  functions which access this file / these files need to specify
+  *which* image they want.  The number of PSPHOT.INPUT entries is
+  saved in config->arguments as PSPHOT.INPUT.NUM (this can be
+  generated from the filerule PSPHOT.INPUT for extension).
+
+  As a result, we need a number of wrapper functions which loop over
+  all PSPHOT.INPUT.NUM entries and perform a particular operation on
+  one of the entries.  Here are the functions which I have modified
+  in this way (function -> child)
+
+  * psphotAddPhotcode -> psphotAddPhotcodeReadout
+    * note that the photcode is now saved on readout->analysis
+  * psphotSetMaskAndVariance -> psphotSetMaskAndVarianceReadout
+  * psphotModelBackground -> psphotModelBackgroundReadoutFileIndex
+  * psphotSubtractBackground -> psphotSubtractBackgroundReadout
+
+  side notes on 
+  
+  * psphotModelBackground vs psphotBackgroundModel : I've renamed
+    psphotBackgroundModel (used only by ppStack for now) to
+    psphotModelBackgroundReadoutNoFile.  I've also named the
+    single-readout version used by psphotModelBackground to
+    psphotModelBackgroundReadoutFileIndex.  These two functions do the
+    same thing, but psphotModelBackgroundReadoutNoFile does not save
+    a pmFPAfile on config->files
+
 
 20090606 : design notes on the multi-image photometry analysis
Index: /branches/eam_branches/20091201/psphot/src/psphot.h
===================================================================
--- /branches/eam_branches/20091201/psphot/src/psphot.h	(revision 26541)
+++ /branches/eam_branches/20091201/psphot/src/psphot.h	(revision 26542)
@@ -40,8 +40,27 @@
 
 // psphotReadout functions
-bool            psphotModelBackground (pmConfig *config, const pmFPAview *view, const char *filename);
-bool            psphotSubtractBackground (pmConfig *config, const pmFPAview *view, const char *filename) ;
+bool            psphotModelBackground (pmConfig *config, const pmFPAview *view);
+bool            psphotModelBackgroundReadoutFileIndex (pmConfig *config, const pmFPAview *view, const char *filename, int index);
+
+// Create a background model for a readout, without saving the result as a pmFPAfile on
+// config->files.  Otherwise identical to psphotModelBackgroundFileIndex.
+psImage        *psphotModelBackgroundReadoutNoFile(pmReadout *readout, const pmConfig *config);
+
+// worker function for above background model functions
+bool psphotModelBackgroundReadout(psImage *model,  // Model image
+				  psImage *modelStdev, // Model stdev image
+				  psMetadata *analysis, // Analysis metadata for outputs
+				  pmReadout *readout, // Readout for which to generate a background model
+				  psImageBinning *binning, // Binning parameters
+				  const pmConfig *config // Configuration
+    );
 
 psImageBinning *psphotBackgroundBinning(const psImage *, const pmConfig *);
+
+
+
+bool            psphotSubtractBackground (pmConfig *config, const pmFPAview *view);
+bool            psphotSubtractBackgroundReadout (pmConfig *config, const pmFPAview *view, const char *filename, int index);
+
 
 pmDetections   *psphotFindDetections (pmDetections *detections, pmReadout *readout, psMetadata *recipe);
@@ -103,5 +122,6 @@
 void            psphotModelClassInit (void);
 bool            psphotGrowthCurve (pmReadout *readout, pmPSF *psf, bool ignore, psImageMaskType maskVal);
-bool            psphotSetMaskAndVariance (pmConfig *config, pmReadout *readout, psMetadata *recipe);
+bool            psphotSetMaskAndVariance (pmConfig *config, const pmFPAview *view, psMetadata *recipe);
+bool            psphotSetMaskAndVarianceReadout (pmConfig *config, pmReadout *readout, psMetadata *recipe);
 void            psphotSourceFreePixels (psArray *sources);
 
@@ -115,5 +135,7 @@
 
 // output functions
-bool            psphotAddPhotcode (psMetadata *recipe, pmConfig *config, const pmFPAview *view, const char *filerule);
+bool            psphotAddPhotcodeReadout (pmConfig *config, const pmFPAview *view, const char *filerule, int index);
+bool            psphotAddPhotcode (pmConfig *config, const pmFPAview *view);
+
 bool            psphotDumpMoments (psMetadata *recipe, psArray *sources);
 psMetadata     *psphotDefineHeader (psMetadata *recipe);
@@ -279,10 +301,4 @@
 
 
-// Create a background model for a readout
-// Essentially identical to psphotModelBackground, but with a more convenient API for outsiders.
-psImage *psphotBackgroundModel(pmReadout *ro, // Readout for which to generate background model
-                               const pmConfig *config // Configuration
-    );
-
 pmConfig *psphotForcedArguments(int argc, char **argv);
 bool psphotForcedImageLoop (pmConfig *config);
Index: /branches/eam_branches/20091201/psphot/src/psphotForcedReadout.c
===================================================================
--- /branches/eam_branches/20091201/psphot/src/psphotForcedReadout.c	(revision 26541)
+++ /branches/eam_branches/20091201/psphot/src/psphotForcedReadout.c	(revision 26542)
@@ -20,5 +20,5 @@
 
     // set the photcode for this image
-    if (!psphotAddPhotcode (recipe, config, view, "PSPHOT.INPUT")) {
+    if (!psphotAddPhotcode (config, view)) {
         psError (PSPHOT_ERR_CONFIG, false, "trouble defining the photcode");
         return false;
@@ -34,5 +34,5 @@
 
     // Generate the mask and weight images, including the user-defined analysis region of interest
-    psphotSetMaskAndVariance (config, readout, recipe);
+    psphotSetMaskAndVariance (config, view, recipe);
     if (!strcasecmp (breakPt, "NOTHING")) {
         return psphotReadoutCleanup(config, readout, recipe, NULL, NULL, NULL);
@@ -43,8 +43,8 @@
 
     // generate a background model (median, smoothed image)
-    if (!psphotModelBackground (config, view, "PSPHOT.INPUT")) {
+    if (!psphotModelBackground (config, view)) {
         return psphotReadoutCleanup (config, readout, recipe, NULL, NULL, NULL);
     }
-    if (!psphotSubtractBackground (config, view, "PSPHOT.INPUT")) {
+    if (!psphotSubtractBackground (config, view)) {
         return psphotReadoutCleanup (config, readout, recipe, NULL, NULL, NULL);
     }
Index: /branches/eam_branches/20091201/psphot/src/psphotMakePSFReadout.c
===================================================================
--- /branches/eam_branches/20091201/psphot/src/psphotMakePSFReadout.c	(revision 26541)
+++ /branches/eam_branches/20091201/psphot/src/psphotMakePSFReadout.c	(revision 26542)
@@ -19,5 +19,5 @@
 
     // set the photcode for this image
-    if (!psphotAddPhotcode (recipe, config, view, "PSPHOT.INPUT")) {
+    if (!psphotAddPhotcode (config, view)) {
         psError (PSPHOT_ERR_CONFIG, false, "trouble defining the photcode");
         return false;
@@ -33,5 +33,5 @@
 
     // Generate the mask and weight images, including the user-defined analysis region of interest
-    psphotSetMaskAndVariance (config, readout, recipe);
+    psphotSetMaskAndVariance (config, view, recipe);
     if (!strcasecmp (breakPt, "NOTHING")) {
         return psphotReadoutCleanup(config, readout, recipe, NULL, NULL, NULL);
@@ -42,8 +42,8 @@
 
     // generate a background model (median, smoothed image)
-    if (!psphotModelBackground (config, view, "PSPHOT.INPUT")) {
+    if (!psphotModelBackground (config, view)) {
         return psphotReadoutCleanup (config, readout, recipe, NULL, NULL, NULL);
     }
-    if (!psphotSubtractBackground (config, view, "PSPHOT.INPUT")) {
+    if (!psphotSubtractBackground (config, view)) {
         return psphotReadoutCleanup (config, readout, recipe, NULL, NULL, NULL);
     }
Index: /branches/eam_branches/20091201/psphot/src/psphotMaskReadout.c
===================================================================
--- /branches/eam_branches/20091201/psphot/src/psphotMaskReadout.c	(revision 26541)
+++ /branches/eam_branches/20091201/psphot/src/psphotMaskReadout.c	(revision 26542)
@@ -2,5 +2,5 @@
 
 // generate mask and variance if not defined, additional mask for restricted subregion
-bool psphotSetMaskAndVariance (pmConfig *config, pmReadout *readout, psMetadata *recipe) {
+bool psphotSetMaskAndVarianceReadout (pmConfig *config, pmReadout *readout, psMetadata *recipe) {
 
     bool status;
@@ -76,8 +76,10 @@
 }
 
-bool psphotStackSetMaskAndVariance (pmConfig *config, const pmFPAview *view, psMetadata *recipe) {
+bool psphotSetMaskAndVariance (pmConfig *config, const pmFPAview *view, psMetadata *recipe) {
 
-    int num = psMetadataAddS32 (&status, config->arguments, "INPUTS.NUM");
-    psAbort (!status, "programming error: must define INPUTS.NUM");
+    bool status = false;
+
+    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
+    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
 
     // loop over the available readouts
@@ -85,4 +87,5 @@
 
         pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PSPHOT.INPUT", i); // File of interest
+	PS_ASSERT_PTR_NON_NULL (file, false);
 
 	// find the currently selected readout
@@ -91,9 +94,12 @@
 
 	// Generate the mask and weight images, including the user-defined analysis region of interest
-	psphotSetMaskAndVariance (config, readout, recipe);
+	if (!psphotSetMaskAndVarianceReadout (config, readout, recipe)) {
+            psError (PSPHOT_ERR_CONFIG, false, "failed to generate mask for PSPHOT.INPUT entry %d", i);
+	    return false;
+	}
 
 	// display the image, weight, mask (ch 1,2,3)
 	psphotVisualShowImage (readout);
     }
-
+    return true;
 }
Index: /branches/eam_branches/20091201/psphot/src/psphotModelBackground.c
===================================================================
--- /branches/eam_branches/20091201/psphot/src/psphotModelBackground.c	(revision 26541)
+++ /branches/eam_branches/20091201/psphot/src/psphotModelBackground.c	(revision 26542)
@@ -30,11 +30,13 @@
 
 // Generate the background model
-static bool backgroundModel(psImage *model,  // Model image
-                            psImage *modelStdev, // Model stdev image
-                            psMetadata *analysis, // Analysis metadata for outputs
-                            pmReadout *readout, // Readout for which to generate a background model
-                            psImageBinning *binning, // Binning parameters
-                            const pmConfig *config // Configuration
-                            )
+// generate the median in NxN boxes, clipping heavily
+// linear interpolation to generate full-scale model
+bool psphotModelBackgroundReadout(psImage *model,  // Model image
+				  psImage *modelStdev, // Model stdev image
+				  psMetadata *analysis, // Analysis metadata for outputs
+				  pmReadout *readout, // Readout for which to generate a background model
+				  psImageBinning *binning, // Binning parameters
+				  const pmConfig *config // Configuration
+    )
 {
     psTimerStart ("psphot.background");
@@ -328,17 +330,16 @@
 }
 
-// XXX these two functions are absurdly-named and very similar -- fix!
-
-psImage *psphotBackgroundModel(pmReadout *ro, const pmConfig *config)
-{
-    PM_ASSERT_READOUT_NON_NULL(ro, NULL);
-    PM_ASSERT_READOUT_IMAGE(ro, NULL);
+// generate a background model for a single readout. do not save an associated pmFPAfile for possible output
+psImage *psphotModelBackgroundReadoutNoFile(pmReadout *readout, const pmConfig *config)
+{
+    PM_ASSERT_READOUT_NON_NULL(readout, NULL);
+    PM_ASSERT_READOUT_IMAGE(readout, NULL);
     PS_ASSERT_PTR_NON_NULL(config, NULL);
 
-    psImageBinning *binning = psphotBackgroundBinning(ro->image, config); // Image binning parameters
+    psImageBinning *binning = psphotBackgroundBinning(readout->image, config); // Image binning parameters
     psImage *model = psImageAlloc(binning->nXruff, binning->nYruff, PS_TYPE_F32); // Background model
     psImage *modelStdev = psImageAlloc(binning->nXruff, binning->nYruff, PS_TYPE_F32); // Standard deviation
 
-    if (!backgroundModel(model, modelStdev, ro->analysis, ro, binning, config)) {
+    if (!psphotModelBackgroundReadout(model, modelStdev, readout->analysis, readout, binning, config)) {
         psFree(model);
         psFree(modelStdev);
@@ -346,19 +347,15 @@
         return NULL;
     }
-
     psFree(modelStdev);
-
     return model;
 }
 
-
-// generate the median in NxN boxes, clipping heavily
-// linear interpolation to generate full-scale model
-bool psphotModelBackground (pmConfig *config, const pmFPAview *view, const char *filename)
-{
-    bool status = true;
-
+// generate a background model for readout number index; save an associated pmFPAfile for possible output
+bool psphotModelBackgroundReadoutFileIndex (pmConfig *config, const pmFPAview *view, const char *filename, int index)
+{
     // find the currently selected readout
-    pmFPAfile *file = psMetadataLookupPtr (&status, config->files, filename);
+    pmFPAfile *file = pmFPAfileSelectSingle(config->files, filename, index); // File of interest
+    PS_ASSERT_PTR_NON_NULL (file, false);
+
     pmFPA *inFPA = file->fpa;
     pmReadout *readout = pmFPAviewThisReadout(view, inFPA);
@@ -368,5 +365,5 @@
     pmReadout *modelStdev = pmFPAGenerateReadout(config, view, "PSPHOT.BACKMDL.STDEV", inFPA, binning);
 
-    if (!backgroundModel(model->image, modelStdev->image, model->analysis, readout, binning, config)) {
+    if (!psphotModelBackgroundReadout(model->image, modelStdev->image, model->analysis, readout, binning, config)) {
         psError(PS_ERR_UNKNOWN, false, "Unable to generate background model");
         return false;
@@ -378,30 +375,17 @@
 
 // XXX supply filename or keep PSPHOT.INPUT fixed?
-bool psphotStackModelBackground (pmConfig *config, const pmFPAview *view)
-{
-    bool status = true;
-
-    int num = psMetadataAddS32 (&status, config->arguments, "INPUTS.NUM");
-    psAbort (!status, "programming error: must define INPUTS.NUM");
+bool psphotModelBackground (pmConfig *config, const pmFPAview *view)
+{
+    bool status = false;
+
+    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
+    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
 
     // loop over the available readouts
     for (int i = 0; i < num; i++) {
-
-        pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PSPHOT.INPUT", i); // File of interest
-
-	// find the currently selected readout
-	pmReadout  *readout = pmFPAviewThisReadout (view, file->fpa);
-	PS_ASSERT_PTR_NON_NULL (readout, false);
-
-	psImageBinning *binning = psphotBackgroundBinning(readout->image, config); // Image binning parameters
-	pmReadout *model = pmFPAGenerateReadout(config, view, "PSPHOT.BACKMDL", inFPA, binning); // this needs to be a MULTI like PSPHOT.INPUT
-	pmReadout *modelStdev = pmFPAGenerateReadout(config, view, "PSPHOT.BACKMDL.STDEV", inFPA, binning); // this needs to be a MULTI like PSPHOT.INPUT
-
-	// XXX save results on model->analysis or readout->analysis??
-	if (!backgroundModel(model->image, modelStdev->image, model->analysis, readout, binning, config)) {
-	    psError(PS_ERR_UNKNOWN, false, "Unable to generate background model");
+	if (!psphotModelBackgroundReadoutFileIndex(config, view, "PSPHOT.INPUT", i)) {
+            psError (PSPHOT_ERR_CONFIG, false, "failed to model background for PSPHOT.INPUT entry %d", i);
 	    return false;
 	}
-	npass ++;
     }
     return true;
Index: /branches/eam_branches/20091201/psphot/src/psphotOutput.c
===================================================================
--- /branches/eam_branches/20091201/psphot/src/psphotOutput.c	(revision 26541)
+++ /branches/eam_branches/20091201/psphot/src/psphotOutput.c	(revision 26542)
@@ -126,19 +126,36 @@
 }
 
-bool psphotAddPhotcode (psMetadata *recipe, pmConfig *config, const pmFPAview *view, const char *filerule) {
-
-    bool status;
-
-    pmFPAfile *input = psMetadataLookupPtr (&status, config->files, filerule);
-    PS_ASSERT (status, false);
-
-    // determine PHOTCODE from fpa & view, overwrite in recipe
-    char *photcode = pmConceptsPhotcodeForView (input, view);
+bool psphotAddPhotcodeReadout (pmConfig *config, const pmFPAview *view, const char *filerule, int index) {
+
+    pmFPAfile *file = pmFPAfileSelectSingle(config->files, filerule, index); // File of interest
+    PS_ASSERT (file, false);
+
+    pmReadout  *readout = pmFPAviewThisReadout (view, file->fpa);
+
+    // determine PHOTCODE from fpa & view, overwrite in readout->analysis
+    char *photcode = pmConceptsPhotcodeForView (file, view);
     PS_ASSERT (photcode, false);
 
-    psMetadataAddStr (recipe, PS_LIST_TAIL, "PHOTCODE", PS_META_REPLACE, "photcode from FPA concepts", photcode);
+    psMetadataAddStr (readout->analysis, PS_LIST_TAIL, "PHOTCODE", PS_META_REPLACE, "photcode from FPA concepts", photcode);
     psLogMsg ("psphot", 3, "PHOTCODE is %s", photcode);
 
     psFree (photcode);
+    return true;
+}
+
+bool psphotAddPhotcode (pmConfig *config, const pmFPAview *view) {
+
+    bool status = false;
+
+    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
+    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+
+    // loop over the available readouts
+    for (int i = 0; i < num; i++) {
+	if (!psphotAddPhotcodeReadout (config, view, "PSPHOT.INPUT", i)) {
+            psError (PSPHOT_ERR_CONFIG, false, "failed to add photcode to PSPHOT.INPUT entry %d", i);
+	    return false;
+	}
+    }
     return true;
 }
@@ -181,4 +198,6 @@
 // these values are saved in an output header stub - they are added to either the
 // PHU header (CMP) or the MEF table header (CMF)
+// XXX these are currently carried by the recipe -- this will not work in a Stack context
+// XXX they should be place in the readout->analysis of the given image
 psMetadata *psphotDefineHeader (psMetadata *recipe) {
 
Index: /branches/eam_branches/20091201/psphot/src/psphotParseCamera.c
===================================================================
--- /branches/eam_branches/20091201/psphot/src/psphotParseCamera.c	(revision 26541)
+++ /branches/eam_branches/20091201/psphot/src/psphotParseCamera.c	(revision 26542)
@@ -39,4 +39,6 @@
         return NULL;
     }
+    // specify the number of psphot input images
+    psMetadataAddS32 (config->arguments, PS_LIST_TAIL, "PSPHOT.INPUT.NUM", PS_META_REPLACE, "number of inputs", 1);
 
     // define the additional input/output files associated with psphot
Index: /branches/eam_branches/20091201/psphot/src/psphotReadout.c
===================================================================
--- /branches/eam_branches/20091201/psphot/src/psphotReadout.c	(revision 26541)
+++ /branches/eam_branches/20091201/psphot/src/psphotReadout.c	(revision 26542)
@@ -28,5 +28,5 @@
 
     // set the photcode for this image
-    if (!psphotAddPhotcode (recipe, config, view, "PSPHOT.INPUT")) {
+    if (!psphotAddPhotcode (config, view)) {
         psError (PSPHOT_ERR_CONFIG, false, "trouble defining the photcode");
         return false;
@@ -42,5 +42,5 @@
 
     // Generate the mask and weight images, including the user-defined analysis region of interest
-    psphotSetMaskAndVariance (config, readout, recipe);
+    psphotSetMaskAndVariance (config, view, recipe);
     if (!strcasecmp (breakPt, "NOTHING")) {
         return psphotReadoutCleanup(config, readout, recipe, NULL, NULL, NULL);
@@ -51,8 +51,8 @@
 
     // generate a background model (median, smoothed image)
-    if (!psphotModelBackground (config, view, "PSPHOT.INPUT")) {
+    if (!psphotModelBackground (config, view)) {
         return psphotReadoutCleanup (config, readout, recipe, NULL, NULL, NULL);
     }
-    if (!psphotSubtractBackground (config, view, "PSPHOT.INPUT")) {
+    if (!psphotSubtractBackground (config, view)) {
         return psphotReadoutCleanup (config, readout, recipe, NULL, NULL, NULL);
     }
Index: /branches/eam_branches/20091201/psphot/src/psphotReadoutFindPSF.c
===================================================================
--- /branches/eam_branches/20091201/psphot/src/psphotReadoutFindPSF.c	(revision 26541)
+++ /branches/eam_branches/20091201/psphot/src/psphotReadoutFindPSF.c	(revision 26542)
@@ -14,6 +14,6 @@
     }
 
-    // set the photcode for this image
-    if (!psphotAddPhotcode(recipe, config, view, "PSPHOT.INPUT")) {
+    // set the photcode for the PSPHOT.INPUT
+    if (!psphotAddPhotcode(config, view)) {
         psError(PSPHOT_ERR_CONFIG, false, "trouble defining the photcode");
         return false;
@@ -25,5 +25,5 @@
 
     // Generate the mask and variance images, including the user-defined analysis region of interest
-    psphotSetMaskAndVariance (config, readout, recipe);
+    psphotSetMaskAndVariance (config, view, recipe);
 
     // display the image, weight, mask (ch 1,2,3)
Index: /branches/eam_branches/20091201/psphot/src/psphotReadoutKnownSources.c
===================================================================
--- /branches/eam_branches/20091201/psphot/src/psphotReadoutKnownSources.c	(revision 26541)
+++ /branches/eam_branches/20091201/psphot/src/psphotReadoutKnownSources.c	(revision 26542)
@@ -15,5 +15,5 @@
 
     // set the photcode for this image
-    if (!psphotAddPhotcode(recipe, config, view, "PSPHOT.INPUT")) {
+    if (!psphotAddPhotcode(config, view)) {
         psError(PSPHOT_ERR_CONFIG, false, "trouble defining the photcode");
         return false;
@@ -25,5 +25,5 @@
 
     // Generate the mask and weight images, including the user-defined analysis region of interest
-    psphotSetMaskAndVariance (config, readout, recipe);
+    psphotSetMaskAndVariance (config, view, recipe);
 
     // display the image, weight, mask (ch 1,2,3)
Index: /branches/eam_branches/20091201/psphot/src/psphotReadoutMinimal.c
===================================================================
--- /branches/eam_branches/20091201/psphot/src/psphotReadoutMinimal.c	(revision 26541)
+++ /branches/eam_branches/20091201/psphot/src/psphotReadoutMinimal.c	(revision 26542)
@@ -25,5 +25,5 @@
 
     // set the photcode for this image
-    if (!psphotAddPhotcode(recipe, config, view, "PSPHOT.INPUT")) {
+    if (!psphotAddPhotcode(config, view)) {
         psError(PSPHOT_ERR_CONFIG, false, "trouble defining the photcode");
         return false;
@@ -35,5 +35,5 @@
 
     // Generate the mask and weight images, including the user-defined analysis region of interest
-    psphotSetMaskAndVariance (config, readout, recipe);
+    psphotSetMaskAndVariance (config, view, recipe);
 
     // display the image, weight, mask (ch 1,2,3)
Index: /branches/eam_branches/20091201/psphot/src/psphotStackParseCamera.c
===================================================================
--- /branches/eam_branches/20091201/psphot/src/psphotStackParseCamera.c	(revision 26541)
+++ /branches/eam_branches/20091201/psphot/src/psphotStackParseCamera.c	(revision 26542)
@@ -56,5 +56,5 @@
     }
     psMetadataRemoveKey(config->arguments, "FILENAMES");
-    psMetadataAddS32 (config->arguments, PS_LIST_TAIL, "INPUTS.NUM", PS_META_REPLACE, "number of inputs", nInputs);
+    psMetadataAddS32 (config->arguments, PS_LIST_TAIL, "PSPHOT.INPUT.NUM", PS_META_REPLACE, "number of inputs", nInputs);
 
     // define the additional input/output files associated with psphot
Index: /branches/eam_branches/20091201/psphot/src/psphotStackReadout.c
===================================================================
--- /branches/eam_branches/20091201/psphot/src/psphotStackReadout.c	(revision 26542)
+++ /branches/eam_branches/20091201/psphot/src/psphotStackReadout.c	(revision 26542)
@@ -0,0 +1,207 @@
+# include "psphotInternal.h"
+
+bool psphotStackReadout (pmConfig *config, const pmFPAview *view) {
+
+    psTimerStart ("psphotReadout");
+
+    pmModelClassSetLimits(PM_MODEL_LIMITS_LAX); // allow models to have ugly fits (eg, central cusp)
+
+    // select the current recipe
+    psMetadata *recipe = psMetadataLookupPtr (NULL, config->recipes, PSPHOT_RECIPE);
+    if (!recipe) {
+        psError(PSPHOT_ERR_CONFIG, false, "missing recipe %s", PSPHOT_RECIPE);
+        return false;
+    }
+
+    // set the photcode for this image (XXX currently goes into recipe, should it go into analysis?)
+    if (!psphotAddPhotcode (config, view)) {
+        psError (PSPHOT_ERR_CONFIG, false, "trouble defining the photcode");
+        return false;
+    }
+
+    // generate a background model (median, smoothed image)
+    if (!psphotSetMaskAndVariance (config, view, recipe)) {
+        return psphotReadoutCleanup (config, NULL, recipe, NULL, NULL, NULL);
+    }
+    if (!strcasecmp (breakPt, "NOTHING")) {
+	return psphotReadoutCleanup(config, NULL, recipe, NULL, NULL, NULL);
+    }
+
+    // optional break-point for processing
+    char *breakPt = psMetadataLookupStr (NULL, recipe, "BREAK_POINT");
+    PS_ASSERT_PTR_NON_NULL (breakPt, false);
+
+    // generate a background model (median, smoothed image)
+    if (!psphotModelBackground (config, view)) {
+        return psphotReadoutCleanup (config, readout, recipe, NULL, NULL, NULL);
+    }
+    if (!psphotSubtractBackground (config, view)) {
+        return psphotReadoutCleanup (config, readout, recipe, NULL, NULL, NULL);
+    }
+    if (!strcasecmp (breakPt, "BACKMDL")) {
+        return psphotReadoutCleanup (config, readout, recipe, NULL, NULL, NULL);
+    }
+
+    // find the detections (by peak and/or footprint) in the image.
+    pmDetections *detections = psphotFindDetections (NULL, readout, recipe);
+    if (!detections) {
+        psLogMsg ("psphot", 3, "unable to find detections in this image");
+        return psphotReadoutCleanup (config, readout, recipe, detections, psf, NULL);
+    }
+
+    // construct sources and measure basic stats
+    psArray *sources = psphotSourceStats (config, readout, detections, true);
+    if (!sources) return false;
+    if (!strcasecmp (breakPt, "PEAKS")) {
+        return psphotReadoutCleanup(config, readout, recipe, detections, psf, sources);
+    }
+
+    // find blended neighbors of very saturated stars
+    // XXX merge this with Basic Deblend?
+    psphotDeblendSatstars (readout, sources, recipe);
+
+    // mark blended peaks PS_SOURCE_BLEND
+    if (!psphotBasicDeblend (sources, recipe)) {
+        psLogMsg ("psphot", 3, "failed on deblend analysis");
+        return psphotReadoutCleanup (config, readout, recipe, detections, psf, sources);
+    }
+
+    // classify sources based on moments, brightness
+    if (!psphotRoughClass (readout, sources, recipe, havePSF)) {
+        psLogMsg ("psphot", 3, "failed to find a valid PSF clump for image");
+        return psphotReadoutCleanup (config, readout, recipe, detections, psf, sources);
+    }
+    if (!strcasecmp (breakPt, "MOMENTS")) {
+        return psphotReadoutCleanup(config, readout, recipe, detections, psf, sources);
+    }
+
+    if (!havePSF && !psphotImageQuality (recipe, sources)) {
+        psLogMsg("psphot", 3, "failed to measure image quality");
+        return psphotReadoutCleanup(config, readout, recipe, detections, psf, sources);
+    }
+
+    // if we were not supplied a PSF, choose one here
+    if (psf == NULL) {
+        // use bright stellar objects to measure PSF
+        // XXX if we do not have enough stars to generate the PSF, build one
+        // from the SEEING guess and model class
+        psf = psphotChoosePSF (readout, sources, recipe);
+        if (psf == NULL) {
+            psLogMsg ("psphot", 3, "failure to construct a psf model");
+            return psphotReadoutCleanup (config, readout, recipe, detections, psf, sources);
+        }
+        havePSF = true;
+    }
+    if (!strcasecmp (breakPt, "PSFMODEL")) {
+        return psphotReadoutCleanup (config, readout, recipe, detections, psf, sources);
+    }
+    psphotVisualShowPSFModel (readout, psf);
+
+    // include externally-supplied sources
+    psphotLoadExtSources (config, view, sources);
+
+    // construct an initial model for each object, set the radius to fitRadius, set circular fit mask
+    psphotGuessModels (config, readout, sources, psf);
+
+    // linear PSF fit to source peaks, subtract the models from the image (in PSF mask)
+    psphotFitSourcesLinear (readout, sources, recipe, psf, FALSE);
+
+    // We have to place these visualizations here because the models are not realized until
+    // psphotGuessModels or fitted until psphotFitSourcesLinear.
+    psphotVisualShowPSFStars (recipe, psf, sources);
+
+    // identify CRs and extended sources
+    psphotSourceSize (config, readout, sources, recipe, psf, 0);
+    if (!strcasecmp (breakPt, "ENSEMBLE")) {
+        goto finish;
+    }
+    psphotVisualShowSatStars (recipe, psf, sources);
+
+    // non-linear PSF and EXT fit to brighter sources
+    // replace model flux, adjust mask as needed, fit, subtract the models (full stamp)
+    psphotBlendFit (config, readout, sources, psf);
+
+    // replace all sources
+    psphotReplaceAllSources (sources, recipe);
+
+    // linear fit to include all sources (subtract again)
+    psphotFitSourcesLinear (readout, sources, recipe, psf, TRUE);
+
+    // if we only do one pass, skip to extended source analysis
+    if (!strcasecmp (breakPt, "PASS1")) {
+        goto pass1finish;
+    }
+    // NOTE: possibly re-measure background model here with objects subtracted
+
+    // add noise for subtracted objects
+    psphotAddNoise (readout, sources, recipe);
+
+    // find fainter sources (pass 2)
+    detections = psphotFindDetections (detections, readout, recipe);
+
+    // remove noise for subtracted objects (ie, return to normal noise level)
+    psphotSubNoise (readout, sources, recipe);
+
+    // define new sources based on only the new peaks
+    psArray *newSources = psphotSourceStats (config, readout, detections, false);
+
+    // set source type
+    if (!psphotRoughClass (readout, newSources, recipe, havePSF)) {
+        psLogMsg ("psphot", 3, "failed to find a valid PSF clump for image");
+        return psphotReadoutCleanup (config, readout, recipe, detections, psf, sources);
+    }
+
+    // create full input models, set the radius to fitRadius, set circular fit mask
+    psphotGuessModels (config, readout, newSources, psf);
+
+    // replace all sources so fit below applies to all at once
+    psphotReplaceAllSources (sources, recipe);
+
+    // merge the newly selected sources into the existing list
+    psphotMergeSources (sources, newSources);
+    psFree (newSources);
+
+    // linear fit to all sources
+    psphotFitSourcesLinear (readout, sources, recipe, psf, TRUE);
+
+pass1finish:
+
+    // measure source size for the remaining sources
+    psphotSourceSize (config, readout, sources, recipe, psf, 0);
+
+    psphotExtendedSourceAnalysis (readout, sources, recipe);
+
+    psphotExtendedSourceFits (readout, sources, recipe);
+
+finish:
+
+    // plot positive sources
+    // psphotSourcePlots (readout, sources, recipe);
+
+    // measure aperture photometry corrections
+    if (!psphotApResid (config, readout, sources, psf)) {
+        psLogMsg ("psphot", 3, "failed on psphotApResid");
+        return psphotReadoutCleanup (config, readout, recipe, detections, psf, sources);
+    }
+
+    // calculate source magnitudes
+    psphotMagnitudes(config, readout, view, sources, psf);
+
+    if (!psphotEfficiency(config, readout, view, psf, recipe, sources)) {
+        psErrorStackPrint(stderr, "Unable to determine detection efficiencies from fake sources");
+        psErrorClear();
+    }
+
+    // replace failed sources?
+    // psphotReplaceUnfitSources (sources);
+
+    // replace background in residual image
+    psphotSkyReplace (config, view);
+
+    // drop the references to the image pixels held by each source
+    psphotSourceFreePixels (sources);
+
+    // create the exported-metadata and free local data
+    return psphotReadoutCleanup(config, readout, recipe, detections, psf, sources);
+}
+
Index: /branches/eam_branches/20091201/psphot/src/psphotSubtractBackground.c
===================================================================
--- /branches/eam_branches/20091201/psphot/src/psphotSubtractBackground.c	(revision 26541)
+++ /branches/eam_branches/20091201/psphot/src/psphotSubtractBackground.c	(revision 26542)
@@ -4,5 +4,5 @@
 // generate the median in NxN boxes, clipping heavily
 // linear interpolation to generate full-scale model
-bool psphotSubtractBackground (pmConfig *config, const pmFPAview *view, const char *filename)
+bool psphotSubtractBackgroundReadout (pmConfig *config, const pmFPAview *view, const char *filename, int index)
 {
     bool status = true;
@@ -13,5 +13,6 @@
 
     // find the currently selected readout
-    pmFPAfile *file = psMetadataLookupPtr (&status, config->files, filename);
+    pmFPAfile *file = pmFPAfileSelectSingle(config->files, filename, index); // File of interest
+
     pmFPA *inFPA = file->fpa;
     pmReadout *readout = pmFPAviewThisReadout (view, inFPA);
@@ -19,6 +20,8 @@
     psImage *mask  = readout->mask;
 
-    // find the currently selected readout
-    pmReadout *model = pmFPAfileThisReadout (config->files, view, "PSPHOT.BACKMDL");
+    // find the currently selected readout (XXX note that the model is saved on PSPHOT.BACKMDL regardless of 'filename'
+    pmFPAfile *modelFile = pmFPAfileSelectSingle(config->files, "PSPHOT.BACKMDL", index); // File of interest
+    assert (modelFile);
+    pmReadout *model = pmFPAviewThisReadout (view, modelFile->fpa);
     assert (model);
 
@@ -35,4 +38,5 @@
 
     // select background pixels, from output background file, or create
+    // XXX for now, we will only allow a single background image to be generated
     file = psMetadataLookupPtr (&status, config->files, "PSPHOT.BACKGND");
     if (file) {
@@ -66,4 +70,5 @@
 
     // back-sub image pixels, from output background file (don't create if not requested)
+    // XXX for now, we will only allow a single background-subtracted image to be generated
     file = psMetadataLookupPtr (&status, config->files, "PSPHOT.BACKSUB");
     if (file) {
@@ -113,35 +118,17 @@
 }
 
-// XXX supply filename or keep PSPHOT.INPUT fixed?
-bool psphotStackSubtractBackground (pmConfig *config, const pmFPAview *view)
+bool psphotSubtractBackground (pmConfig *config, const pmFPAview *view)
 {
-    bool status = true;
+    bool status = false;
 
-    int num = psMetadataAddS32 (&status, config->arguments, "INPUTS.NUM");
-    psAbort (!status, "programming error: must define INPUTS.NUM");
+    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
+    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
 
     // loop over the available readouts
     for (int i = 0; i < num; i++) {
-
-        pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PSPHOT.INPUT", i); // File of interest
-
-	// find the currently selected readout
-	pmReadout  *readout = pmFPAviewThisReadout (view, file->fpa);
-	PS_ASSERT_PTR_NON_NULL (readout, false);
-
-	psImageBinning *binning = psphotBackgroundBinning(readout->image, config); // Image binning parameters
-	pmReadout *model = pmFPAGenerateReadout(config, view, "PSPHOT.BACKMDL", inFPA, binning); // this needs to be a MULTI like PSPHOT.INPUT
-	pmReadout *modelStdev = pmFPAGenerateReadout(config, view, "PSPHOT.BACKMDL.STDEV", inFPA, binning); // this needs to be a MULTI like PSPHOT.INPUT
-
-	// XXX save results on model->analysis or readout->analysis??
-	// XXX need to create a worker function from the function above...
-	if (!backgroundModel(model->image, modelStdev->image, model->analysis, readout, binning, config)) {
-	    psError(PS_ERR_UNKNOWN, false, "Unable to generate background model");
+	if (!psphotSubtractBackgroundReadout (config, view, "PSPHOT.INPUT", i)) {
+            psError (PSPHOT_ERR_CONFIG, false, "failed to subtract background for PSPHOT.INPUT entry %d", i);
 	    return false;
 	}
-	// display the backsub and backgnd images
-	psphotVisualShowBackground (config, view, readout);
-
-	npass ++;
     }
     return true;
