Index: trunk/psphot/src/Makefile.am
===================================================================
--- trunk/psphot/src/Makefile.am	(revision 29902)
+++ trunk/psphot/src/Makefile.am	(revision 29936)
@@ -103,16 +103,4 @@
 	psphotCleanup.c
 
-
-
-# # psphot analysis of the detectability of specified positions
-# psphotDetect_SOURCES =            \
-#         psphotDetect.c            \
-# 	psphotDetectArguments.c	  \
-# 	psphotDetectParseCamera.c \
-# 	psphotDetectImageLoop.c	  \
-# 	psphotDetectReadout.c	  \
-# 	psphotMosaicChip.c	  \
-# 	psphotCleanup.c
-
 # psphotTest_SOURCES = \
 #         psphotTest.c
@@ -137,4 +125,5 @@
 	psphotReadoutFindPSF.c	       \
 	psphotReadoutKnownSources.c    \
+	psphotReadoutForcedKnownSources.c    \
 	psphotReadoutMinimal.c	       \
 	psphotModelBackground.c	       \
Index: trunk/psphot/src/psphot.h
===================================================================
--- trunk/psphot/src/psphot.h	(revision 29902)
+++ trunk/psphot/src/psphot.h	(revision 29936)
@@ -24,8 +24,9 @@
 bool            psphotModelTest (pmConfig *config, const pmFPAview *view, psMetadata *recipe);
 bool            psphotInit (void);
-bool            psphotReadout (pmConfig *config, const pmFPAview *view);
-bool            psphotReadoutFindPSF(pmConfig *config, const pmFPAview *view, psArray *inSources);
-bool            psphotReadoutKnownSources(pmConfig *config, const pmFPAview *view, psArray *inSources);
-bool            psphotReadoutMinimal(pmConfig *config, const pmFPAview *view);
+bool            psphotReadout (pmConfig *config, const pmFPAview *view, const char *filerule);
+bool            psphotReadoutFindPSF(pmConfig *config, const pmFPAview *view, const char *filerule, psArray *inSources);
+bool            psphotReadoutKnownSources(pmConfig *config, const pmFPAview *view, const char *filerule, psArray *inSources);
+bool            psphotReadoutForcedKnownSources(pmConfig *config, const pmFPAview *view, const char *filerule, psArray *inSources);
+bool            psphotReadoutMinimal(pmConfig *config, const pmFPAview *view, const char *filerule);
 
 bool            psphotReadoutCleanup (pmConfig *config, const pmFPAview *view, const char *filerule);
@@ -219,5 +220,5 @@
 bool            psphotFitSummary (void);
 
-bool            psphotLoadPSF (pmConfig *config, const pmFPAview *view);
+bool            psphotLoadPSF (pmConfig *config, const pmFPAview *view, const char *filerule);
 bool            psphotLoadPSFReadout (pmConfig *config, const pmFPAview *view, const char *outFilename, const char *inFilename, int index);
 
@@ -304,9 +305,14 @@
 pmConfig *psphotForcedArguments(int argc, char **argv);
 bool psphotForcedImageLoop (pmConfig *config);
-bool psphotForcedReadout(pmConfig *config, const pmFPAview *view);
+bool psphotForcedReadout(pmConfig *config, const pmFPAview *view, const char *filerule);
 
 pmConfig *psphotMakePSFArguments(int argc, char **argv);
 bool psphotMakePSFImageLoop (pmConfig *config);
-bool psphotMakePSFReadout(pmConfig *config, const pmFPAview *view);
+bool psphotMakePSFReadout(pmConfig *config, const pmFPAview *view, const char *filerule);
+
+int psphotFileruleCount(const pmConfig *config, const char *filerule);
+
+bool psphotAddKnownSources (pmConfig *config, const pmFPAview *view, const char *filerule, psArray *inSources);
+
 
 /**** psphotStack prototypes ****/
@@ -432,3 +438,5 @@
 pmModel *psphotFitPCM (pmReadout *readout, pmSource *source, pmSourceFitOptions *fitOptions, pmModelType modelType, psImageMaskType maskVal, psImageMaskType markVal, int psfSize);
 
+bool psphotCleanInputs (pmConfig *config, const pmFPAview *view, const char *filerule);
+
 #endif
Index: trunk/psphot/src/psphotAddNoise.c
===================================================================
--- trunk/psphot/src/psphotAddNoise.c	(revision 29902)
+++ trunk/psphot/src/psphotAddNoise.c	(revision 29936)
@@ -18,6 +18,5 @@
     psAssert (recipe, "missing recipe?");
 
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // loop over the available readouts
Index: trunk/psphot/src/psphotApResid.c
===================================================================
--- trunk/psphot/src/psphotApResid.c	(revision 29902)
+++ trunk/psphot/src/psphotApResid.c	(revision 29936)
@@ -13,6 +13,5 @@
     psAssert (recipe, "missing recipe?");
 
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // skip the chisq image (optionally?)
@@ -72,8 +71,8 @@
     if (!measureAptrend) {
         // save nan values since these were not calculated
-        psMetadataAdd (readout->analysis, PS_LIST_TAIL, "APMIFIT",  PS_DATA_F32 | PS_META_REPLACE, "aperture residual",   NAN);
-        psMetadataAdd (readout->analysis, PS_LIST_TAIL, "DAPMIFIT", PS_DATA_F32 | PS_META_REPLACE, "ap residual scatter", NAN);
-        psMetadataAdd (readout->analysis, PS_LIST_TAIL, "NAPMIFIT", PS_DATA_S32 | PS_META_REPLACE, "number of apresid stars", 0);
-        psMetadataAdd (readout->analysis, PS_LIST_TAIL, "APLOSS",   PS_DATA_F32 | PS_META_REPLACE, "aperture loss (mag)", NAN);
+        psMetadataAddF32 (readout->analysis, PS_LIST_TAIL, "APMIFIT",  PS_META_REPLACE, "aperture residual",   NAN);
+        psMetadataAddF32 (readout->analysis, PS_LIST_TAIL, "DAPMIFIT", PS_META_REPLACE, "ap residual scatter", NAN);
+        psMetadataAddS32 (readout->analysis, PS_LIST_TAIL, "NAPMIFIT", PS_META_REPLACE, "number of apresid stars", 0);
+        psMetadataAddF32 (readout->analysis, PS_LIST_TAIL, "APLOSS",   PS_META_REPLACE, "aperture loss (mag)", NAN);
         return true;
     }
@@ -325,8 +324,8 @@
 
     // save results for later output
-    psMetadataAdd (readout->analysis, PS_LIST_TAIL, "APMIFIT",  PS_DATA_F32 | PS_META_REPLACE, "aperture residual",   psf->ApResid);
-    psMetadataAdd (readout->analysis, PS_LIST_TAIL, "DAPMIFIT", PS_DATA_F32 | PS_META_REPLACE, "ap residual scatter", psf->dApResid);
-    psMetadataAdd (readout->analysis, PS_LIST_TAIL, "NAPMIFIT", PS_DATA_S32 | PS_META_REPLACE, "number of apresid stars", psf->nApResid);
-    psMetadataAdd (readout->analysis, PS_LIST_TAIL, "APLOSS",   PS_DATA_F32 | PS_META_REPLACE, "aperture loss (mag)", psf->growth ? psf->growth->apLoss : NAN);
+    psMetadataAddF32 (readout->analysis, PS_LIST_TAIL, "APMIFIT",  PS_META_REPLACE, "aperture residual",   psf->ApResid);
+    psMetadataAddF32 (readout->analysis, PS_LIST_TAIL, "DAPMIFIT", PS_META_REPLACE, "ap residual scatter", psf->dApResid);
+    psMetadataAddS32 (readout->analysis, PS_LIST_TAIL, "NAPMIFIT", PS_META_REPLACE, "number of apresid stars", psf->nApResid);
+    psMetadataAddF32 (readout->analysis, PS_LIST_TAIL, "APLOSS",   PS_META_REPLACE, "aperture loss (mag)", psf->growth ? psf->growth->apLoss : NAN);
 
     psLogMsg ("psphot.apresid", PS_LOG_DETAIL, "aperture residual: %f +/- %f\n", psf->ApResid, psf->dApResid);
@@ -345,8 +344,8 @@
 escape:
     // save nan values since these were not calculated
-    psMetadataAdd (readout->analysis, PS_LIST_TAIL, "APMIFIT",  PS_DATA_F32 | PS_META_REPLACE, "aperture residual",   NAN);
-    psMetadataAdd (readout->analysis, PS_LIST_TAIL, "DAPMIFIT", PS_DATA_F32 | PS_META_REPLACE, "ap residual scatter", NAN);
-    psMetadataAdd (readout->analysis, PS_LIST_TAIL, "NAPMIFIT", PS_DATA_S32 | PS_META_REPLACE, "number of apresid stars", 0);
-    psMetadataAdd (readout->analysis, PS_LIST_TAIL, "APLOSS",   PS_DATA_F32 | PS_META_REPLACE, "aperture loss (mag)", NAN);
+    psMetadataAddF32 (readout->analysis, PS_LIST_TAIL, "APMIFIT",  PS_META_REPLACE, "aperture residual",   NAN);
+    psMetadataAddF32 (readout->analysis, PS_LIST_TAIL, "DAPMIFIT", PS_META_REPLACE, "ap residual scatter", NAN);
+    psMetadataAddS32 (readout->analysis, PS_LIST_TAIL, "NAPMIFIT", PS_META_REPLACE, "number of apresid stars", 0);
+    psMetadataAddF32 (readout->analysis, PS_LIST_TAIL, "APLOSS",   PS_META_REPLACE, "aperture loss (mag)", NAN);
 
     psFree (xPos);
Index: trunk/psphot/src/psphotBasicDeblend.c
===================================================================
--- trunk/psphot/src/psphotBasicDeblend.c	(revision 29902)
+++ trunk/psphot/src/psphotBasicDeblend.c	(revision 29936)
@@ -4,8 +4,5 @@
 bool psphotBasicDeblend (pmConfig *config, const pmFPAview *view, const char *filerule)
 {
-    bool status = true;
-
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // loop over the available readouts
Index: trunk/psphot/src/psphotBlendFit.c
===================================================================
--- trunk/psphot/src/psphotBlendFit.c	(revision 29902)
+++ trunk/psphot/src/psphotBlendFit.c	(revision 29936)
@@ -10,6 +10,5 @@
     psAssert (recipe, "missing recipe?");
 
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // loop over the available readouts
Index: trunk/psphot/src/psphotChoosePSF.c
===================================================================
--- trunk/psphot/src/psphotChoosePSF.c	(revision 29902)
+++ trunk/psphot/src/psphotChoosePSF.c	(revision 29936)
@@ -10,6 +10,5 @@
     psAssert (recipe, "missing recipe?");
 
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // skip the chisq image (optionally?)
Index: trunk/psphot/src/psphotDeblendSatstars.c
===================================================================
--- trunk/psphot/src/psphotDeblendSatstars.c	(revision 29902)
+++ trunk/psphot/src/psphotDeblendSatstars.c	(revision 29936)
@@ -4,8 +4,5 @@
 bool psphotDeblendSatstars (pmConfig *config, const pmFPAview *view, const char *filerule)
 {
-    bool status = true;
-
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // loop over the available readouts
Index: trunk/psphot/src/psphotDetect.c
===================================================================
--- trunk/psphot/src/psphotDetect.c	(revision 29902)
+++ 	(revision )
@@ -1,41 +1,0 @@
-# include "psphotDetect.h"
-
-static void usage (void) {
-    fprintf (stderr, "USAGE: psphotDetect [-mask image(s)] [-masklist masklist] [-psf psfmodel] [-points pointlist] (output)\n");
-    exit (PS_EXIT_CONFIG_ERROR);
-}
-
-int main (int argc, char **argv) {
-
-    psTimerStart ("complete");
-    pmErrorRegister();			// register psModule's error codes/messages
-    psphotInit();
-
-    // load command-line arguments, options, and system config data
-    pmConfig *config = psphotDetectArguments (argc, argv);
-    if (!config) {
-	psErrorStackPrint(stderr, "Error reading arguments\n");
-	usage ();
-    }
-
-    // load input data (config and images (signal, noise, mask)
-    if (!psphotDetectParseCamera (config)) {
-        psErrorStackPrint(stderr, "Error setting up the camera\n");
-        exit (psphotGetExitStatus());
-    }
-
-    // call psphot for each readout
-    if (!psphotDetectImageLoop (config)) {
-        psErrorStackPrint(stderr, "Error in the psphotDetect image loop\n");
-        exit (psphotGetExitStatus());
-    }
-
-    psLogMsg ("psphot", 3, "complete psphotDetect run: %f sec\n", psTimerMark ("complete"));
-
-    psErrorCode exit_status = psphotGetExitStatus();
-    psphotCleanup (config);
-    exit (exit_status);
-}
-
-// all functions which return to this level must raise one of the top-level error codes if they
-// exit with an error.  these error codes are used to specify the program exit status
Index: trunk/psphot/src/psphotDetect.h
===================================================================
--- trunk/psphot/src/psphotDetect.h	(revision 29902)
+++ 	(revision )
@@ -1,23 +1,0 @@
-# ifdef HAVE_CONFIG_H
-# include <config.h>
-# endif
-
-#ifndef PSPHOT_DETECT_H
-#define PSPHOT_DETECT_H
-
-#include <stdio.h>
-#include <pslib.h>
-#include <psmodules.h>
-#include "psphot.h"
-
-// Top level functions
-pmConfig   *psphotDetectArguments(int argc, char **argv);
-bool        psphotDetectParseCamera (pmConfig *config);
-bool        psphotDetectImageLoop (pmConfig *config);
-bool        psphotDetectReadout(pmConfig *config, const pmFPAview *view);
-
-bool        psphotMosaicChip(pmConfig *config, const pmFPAview *view, char *outFile, char *inFile);
-void        psphotCleanup (pmConfig *config);
-psExit      psphotGetExitStatus (void);
-
-#endif
Index: trunk/psphot/src/psphotDetectArguments.c
===================================================================
--- trunk/psphot/src/psphotDetectArguments.c	(revision 29902)
+++ 	(revision )
@@ -1,57 +1,0 @@
-# include "psphotDetect.h"
-
-pmConfig *psphotDetectArguments(int argc, char **argv) {
-
-    int N;
-
-    if (argc == 1) {
-        psError(PSPHOT_ERR_ARGUMENTS, true, "Too few arguments: %d", argc);
-        return NULL;
-    }
-
-    if ((N = psArgumentGet (argc, argv, "-version"))) {
-        psString version;
-        version = psphotVersionLong();    fprintf (stdout, "%s\n", version); psFree (version);
-        version = psModulesVersionLong(); fprintf (stdout, "%s\n", version); psFree (version);
-        version = psLibVersionLong();     fprintf (stdout, "%s\n", version); psFree (version);
-        exit (0);
-    }
-
-    // load config data from default locations
-    pmConfig *config = pmConfigRead(&argc, argv, PSPHOT_RECIPE);
-    if (config == NULL) {
-        psError(PSPHOT_ERR_CONFIG, false, "Can't read site configuration");
-        return NULL;
-    }
-
-    // save the following additional recipe values based on command-line options
-    // these options override the PSPHOT recipe values loaded from recipe files
-    psMetadata *options = pmConfigRecipeOptions (config, PSPHOT_RECIPE);
-
-    // XXX add psphot thread arguments?
-
-    // all three of these input files are required: if not found, we will exit
-    if (!pmConfigFileSetsMD (config->arguments, &argc, argv, "MASK",   "-mask",    "-masklist")) {
-        psError(PSPHOT_ERR_ARGUMENTS, false, "mask image not supplied");
-        return NULL;
-    }
-    if (!pmConfigFileSetsMD (config->arguments, &argc, argv, "PSPHOT.PSF", "-psf", "-psflist")) {
-        psError(PSPHOT_ERR_ARGUMENTS, false, "mask image not supplied");
-        return NULL;
-    }
-    if (!pmConfigFileSetsMD (config->arguments, &argc, argv, "POINTS", "-points",  "-pointslist")) {
-        psError(PSPHOT_ERR_ARGUMENTS, false, "mask image not supplied");
-        return NULL;
-    }
-
-    if (argc != 2) {
-        psError(PSPHOT_ERR_ARGUMENTS, true, "Expected to see one more argument; saw %d", argc - 1);
-        return NULL;
-    }
-
-    // output position is fixed
-    psMetadataAddStr (config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "", argv[1]);
-
-    psTrace("psphot", 1, "Done with psphotDetectArguments...\n");
-    return (config);
-}
Index: trunk/psphot/src/psphotDetectImageLoop.c
===================================================================
--- trunk/psphot/src/psphotDetectImageLoop.c	(revision 29902)
+++ 	(revision )
@@ -1,97 +1,0 @@
-# include "psphotDetect.h"
-
-# define ESCAPE(MESSAGE) { \
-  psError(PSPHOT_ERR_DATA, false, MESSAGE); \
-  psFree (view); \
-  return false; \
-}
-
-bool psphotDetectImageLoop (pmConfig *config) {
-
-    bool status;
-    pmChip *chip;
-    pmCell *cell;
-    pmReadout *readout;
-
-    pmFPAfile *mask = psMetadataLookupPtr (&status, config->files, "PSPHOT.MASK");
-    if (!status) {
-	psError(PSPHOT_ERR_PROG, false, "Can't find input mask data!");
-	return false;
-    }
-    pmFPAfile *input = psMetadataLookupPtr (&status, config->files, "PSPHOT.INPUT");
-    if (!status) {
-	psError(PSPHOT_ERR_PROG, false, "Can't find input data!");
-	return false;
-    }
-
-    pmFPAview *view = pmFPAviewAlloc (0);
-    
-    // files associated with the science image
-    if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE ("failed input for fpa in psphot.");
-
-    // for psphot, we force data to be read at the chip level 
-    while ((chip = pmFPAviewNextChip (view, mask->fpa, 1)) != NULL) {
-        psLogMsg ("psphot", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
-        if (!chip->process || !chip->file_exists) { continue; }
-
-	// load just the input image data (image, mask, weight)
-	pmFPAfileActivate (config->files, false, NULL);
-	pmFPAfileActivate (config->files, true, "PSPHOT.MASK");
-	if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE ("failed input for Chip in psphot.");
-
-	// mosaic the cells of a chip into a single contiguous (trimmed) chip
-        if (!psphotMosaicChip(config, view, "PSPHOT.INPUT", "PSPHOT.MASK")) ESCAPE ("Unable to mosaic chip.");
-
-	// try to load other supporting data (PSF, SRC, etc).
-	// do not re-load the mask three files
-	pmFPAfileActivate (config->files, true, NULL);
-	pmFPAfileActivate (config->files, false, "PSPHOT.MASK");
-	if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE ("failed input for Chip in psphot.");
-
-	// re-activate files so they will be closed and freed below
-	pmFPAfileActivate (config->files, true, NULL);
-
-	// there is now only a single chip (multiple readouts?). loop over it and process
-	while ((cell = pmFPAviewNextCell (view, input->fpa, 1)) != NULL) {
-            psLogMsg ("psphot", 5, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
-
-	    // process each of the readouts
-	    while ((readout = pmFPAviewNextReadout (view, input->fpa, 1)) != NULL) {
-		psLogMsg ("psphot", 6, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
-		if (!readout->data_exists) { continue; }
-
-		// run the actual photometry analysis on this chip/cell/readout
-		if (!psphotDetectReadout (config, view)) {
-		    psError(psErrorCodeLast(), false, "failure in psphotDetectReadout for chip %d, cell %d, readout %d\n", view->chip, view->cell, view->readout);
-		    psFree (view);
-		    return false;
-		}
-	    }
-	}
-
-	// save output which is saved at the chip level
-	if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE ("failed output for Chip in psphot.");
-    }
-    // save output which is saved at the fpa level
-    if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE ("failed ouput for FPA in psphot.");
-
-    // fail if we failed to handle an error
-    if (psErrorCodeLast() != PS_ERR_NONE) psAbort ("failed to handle an error!");
-
-    psFree (view);
-    return true;
-}
-
-// I/O files related to psphot:
-// PSPHOT.INPUT   : input image file(s)
-// PSPHOT.RESID   : residual image
-// PSPHOT.OUTPUT  : output object tables (object)
-
-// PSPHOT.BACKSUB : background subtracted image
-// PSPHOT.BACKGND : background model (full-scale image?)
-// PSPHOT.BACKMDL : background model (binned image?)
-// PSPHOT.PSF     : sample PSF images
-
-// PSPHOT.MASK
-// PSPHOT.WEIGHT
-// 
Index: trunk/psphot/src/psphotDetectParseCamera.c
===================================================================
--- trunk/psphot/src/psphotDetectParseCamera.c	(revision 29902)
+++ 	(revision )
@@ -1,43 +1,0 @@
-# include "psphotDetect.h"
-
-// define the needed / desired I/O files
-bool psphotDetectParseCamera (pmConfig *config) {
-
-    bool status = false;
-
-    // if MASK or WEIGHT was supplied on command line, bind files to 'load'
-    // the mask and weight will be mosaicked with the image
-    pmFPAfile *mask = pmFPAfileDefineFromArgs (&status, config, "PSPHOT.MASK", "MASK");
-    if (!status) {
-        psError (PS_ERR_UNKNOWN, false, "failed to load find definition");
-        return NULL;
-    }
-    if (!psphotSetMaskBits (config)) {
-      // XXX shouldn't this come after the file has been read?
-        psError (PS_ERR_UNKNOWN, false, "failed to set mask bit values");
-        return NULL;
-    }
-    mask->dataLevel = PM_FPA_LEVEL_CHIP; // force load at the CHIP level
-
-    // the psphot analysis is performed on chips
-    pmFPAfile *input = pmFPAfileDefineChipMosaic(config, mask->fpa, "PSPHOT.INPUT");
-    if (!input) {
-        psError(PSPHOT_ERR_CONFIG, false, _("Unable to generate new file from PSPHOT.INPUT"));
-        return NULL;
-    }
-
-    // the PSF model is required
-    pmFPAfileBindFromArgs(&status, mask, config, "PSPHOT.PSF.LOAD", "PSPHOT.PSF");
-    if (!status) {
-      psError(PSPHOT_ERR_CONFIG, false, "Failed to find/build PSPHOT.PSF.LOAD");
-      return status;
-    }
-
-    if (!pmFPAfileDefineFromArgs (&status, config, "PSPHOT.INPUT.PTS", "POINTS")) {
-      psError(PSPHOT_ERR_CONFIG, false, "Failed to find/build PSPHOT.INPUT.PTS");
-      return status;
-    }
-
-    psErrorClear();                     // some metadata lookup may have failed
-    return true;
-}
Index: trunk/psphot/src/psphotDetectReadout.c
===================================================================
--- trunk/psphot/src/psphotDetectReadout.c	(revision 29902)
+++ 	(revision )
@@ -1,54 +1,0 @@
-# include "psphotInternal.h"
-
-bool psphotDetectReadout(pmConfig *config, const pmFPAview *view) {
-
-    // measure the total elapsed time in psphotReadout.  XXX the current threading plan
-    // for psphot envisions threading within psphotReadout, not multiple threads calling
-    // the same psphotReadout.  In the current plan, this dtime is the elapsed time used
-    // jointly by the multiple threads, not the total time used by all threads.
-    psTimerStart ("psphotReadout");
-
-    // 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;
-    }
-
-    // find the currently selected readout
-    pmReadout  *readout = pmFPAfileThisReadout (config->files, view, "PSPHOT.INPUT");
-    PS_ASSERT_PTR_NON_NULL (readout, false);
-
-    // Generate the mask and weight images, including the user-defined analysis region of interest
-    psphotSetMaskAndWeight (config, readout, recipe);
-
-    // load the psf model, if suppled.  FWHM_X,FWHM_Y,etc are saved in the recipe
-    pmPSF *psf = psphotLoadPSF (config, view, recipe);
-    psAssert (psf, "psf should be loaded");
-
-    // grab the sources of interest from the storage location (pmFPAfile PSPHOT.INPUT.CMF)
-    psArray *sources = psphotLoadPSFSources (config, view);
-    if (!sources) {
-      psError(PS_ERR_UNKNOWN, false, "No sources supplied to measure PSF");
-      return false;
-    }
-
-    // include externally-supplied sources (supplied as PSPHOT.INPUT.CMF)
-    psphotSetSourceParams (config, sources, psf);
-
-    // calculate source magnitudes
-    psphotPSFWeights(config, readout, view, sources);
-
-    // create the exported-metadata and free local data
-    return psphotReadoutCleanup(config, readout, recipe, detections, psf, sources);
-}
-
-// deep in pmSourcePixelWeight, we need the following items for each location of interest:
-// pmModel *model (should be a realized version of the PSF, can have unity normalization)
-// psImage *mask  (local subimage of mask for source)
-// psImageMaskType maskVal (from recipe & mask header)
-
-{ 
-    pmModel *model = pmModelFromPSFforXY (psf, x, y, 1.0);
-}
-
Index: trunk/psphot/src/psphotEfficiency.c
===================================================================
--- trunk/psphot/src/psphotEfficiency.c	(revision 29902)
+++ trunk/psphot/src/psphotEfficiency.c	(revision 29936)
@@ -164,6 +164,5 @@
     psAssert (recipe, "missing recipe?");
 
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // skip the chisq image (optionally?)
Index: trunk/psphot/src/psphotExtendedSourceAnalysis.c
===================================================================
--- trunk/psphot/src/psphotExtendedSourceAnalysis.c	(revision 29902)
+++ trunk/psphot/src/psphotExtendedSourceAnalysis.c	(revision 29936)
@@ -22,6 +22,5 @@
     }
 
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // loop over the available readouts
Index: trunk/psphot/src/psphotExtendedSourceAnalysisByObject.c
===================================================================
--- trunk/psphot/src/psphotExtendedSourceAnalysisByObject.c	(revision 29902)
+++ trunk/psphot/src/psphotExtendedSourceAnalysisByObject.c	(revision 29936)
@@ -41,6 +41,5 @@
 
     // number of images used to define sources
-    int nImages = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int nImages = psphotFileruleCount(config, filerule);
 
     // generate look-up arrays for readouts
Index: trunk/psphot/src/psphotExtendedSourceFits.c
===================================================================
--- trunk/psphot/src/psphotExtendedSourceFits.c	(revision 29902)
+++ trunk/psphot/src/psphotExtendedSourceFits.c	(revision 29936)
@@ -16,6 +16,5 @@
     }
 
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // loop over the available readouts
Index: trunk/psphot/src/psphotFindDetections.c
===================================================================
--- trunk/psphot/src/psphotFindDetections.c	(revision 29902)
+++ trunk/psphot/src/psphotFindDetections.c	(revision 29936)
@@ -12,6 +12,5 @@
     psAssert (recipe, "missing recipe?");
 
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // loop over the available readouts
Index: trunk/psphot/src/psphotFitSourcesLinear.c
===================================================================
--- trunk/psphot/src/psphotFitSourcesLinear.c	(revision 29902)
+++ trunk/psphot/src/psphotFitSourcesLinear.c	(revision 29936)
@@ -21,6 +21,5 @@
     assert (recipe);
 
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // loop over the available readouts
Index: trunk/psphot/src/psphotForcedImageLoop.c
===================================================================
--- trunk/psphot/src/psphotForcedImageLoop.c	(revision 29902)
+++ trunk/psphot/src/psphotForcedImageLoop.c	(revision 29936)
@@ -84,5 +84,5 @@
 
                 // run the actual photometry analysis on this chip/cell/readout
-                if (!psphotForcedReadout (config, view)) {
+                if (!psphotForcedReadout (config, view, "PSPHOT.INPUT")) {
                     psError(psErrorCodeLast(), false, "failure in psphotReadout for chip %d, cell %d, readout %d\n", view->chip, view->cell, view->readout);
                     psFree (view);
Index: trunk/psphot/src/psphotForcedReadout.c
===================================================================
--- trunk/psphot/src/psphotForcedReadout.c	(revision 29902)
+++ trunk/psphot/src/psphotForcedReadout.c	(revision 29936)
@@ -1,5 +1,5 @@
 # include "psphotInternal.h"
 
-bool psphotForcedReadout(pmConfig *config, const pmFPAview *view) {
+bool psphotForcedReadout(pmConfig *config, const pmFPAview *view, const char *filerule) {
 
     // measure the total elapsed time in psphotReadout.  XXX the current threading plan
@@ -20,5 +20,5 @@
 
     // set the photcode for this image
-    if (!psphotAddPhotcode (config, view, "PSPHOT.INPUT")) {
+    if (!psphotAddPhotcode (config, view, filerule)) {
         psError (PSPHOT_ERR_CONFIG, false, "trouble defining the photcode");
         return false;
@@ -30,38 +30,38 @@
 
     // Generate the mask and weight images, including the user-defined analysis region of interest
-    psphotSetMaskAndVariance (config, view, "PSPHOT.INPUT");
+    psphotSetMaskAndVariance (config, view, filerule);
     if (!strcasecmp (breakPt, "NOTHING")) {
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
     // generate a background model (median, smoothed image)
-    if (!psphotModelBackground (config, view, "PSPHOT.INPUT")) {
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+    if (!psphotModelBackground (config, view, filerule)) {
+        return psphotReadoutCleanup (config, view, filerule);
     }
-    if (!psphotSubtractBackground (config, view, "PSPHOT.INPUT")) {
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+    if (!psphotSubtractBackground (config, view, filerule)) {
+        return psphotReadoutCleanup (config, view, filerule);
     }
     if (!strcasecmp (breakPt, "BACKMDL")) {
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
-    if (!psphotLoadPSF (config, view)) {
+    if (!psphotLoadPSF (config, view, filerule)) {
     	// this only happens if we had a programming error in psphotLoadPSF
         psError (PSPHOT_ERR_UNKNOWN, false, "error loading psf model");
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
     // include externally-supplied sources
-    psphotLoadExtSources (config, view, "PSPHOT.INPUT");
+    psphotLoadExtSources (config, view, filerule);
 
     // construct an initial model for each object, set the radius to fitRadius, set circular fit mask
-    psphotGuessModels (config, view, "PSPHOT.INPUT");
+    psphotGuessModels (config, view, filerule);
 
     // merge the newly selected sources into the existing list
     // NOTE: merge OLD and NEW
-    psphotMergeSources (config, view, "PSPHOT.INPUT");
+    psphotMergeSources (config, view, filerule);
 
     // linear PSF fit to source peaks, subtract the models from the image (in PSF mask)
-    psphotFitSourcesLinear (config, view, "PSPHOT.INPUT", false);
+    psphotFitSourcesLinear (config, view, filerule, false);
 
     // identify CRs and extended sources
@@ -71,5 +71,5 @@
 
     // calculate source magnitudes
-    psphotMagnitudes(config, view, "PSPHOT.INPUT");
+    psphotMagnitudes(config, view, filerule);
 
     // XXX do I want to do this?
@@ -80,10 +80,10 @@
 
     // replace background in residual image
-    psphotSkyReplace (config, view, "PSPHOT.INPUT");
+    psphotSkyReplace (config, view, filerule);
 
     // drop the references to the image pixels held by each source
-    psphotSourceFreePixels (config, view, "PSPHOT.INPUT");
+    psphotSourceFreePixels (config, view, filerule);
 
     // create the exported-metadata and free local data
-    return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+    return psphotReadoutCleanup (config, view, filerule);
 }
Index: trunk/psphot/src/psphotGuessModels.c
===================================================================
--- trunk/psphot/src/psphotGuessModels.c	(revision 29902)
+++ trunk/psphot/src/psphotGuessModels.c	(revision 29936)
@@ -12,6 +12,5 @@
     bool status = true;
 
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // skip the chisq image (optionally?)
Index: trunk/psphot/src/psphotImageLoop.c
===================================================================
--- trunk/psphot/src/psphotImageLoop.c	(revision 29902)
+++ trunk/psphot/src/psphotImageLoop.c	(revision 29936)
@@ -109,5 +109,5 @@
 
                 // run the actual photometry analysis on this chip/cell/readout
-                if (!psphotReadout (config, view)) {
+                if (!psphotReadout (config, view, "PSPHOT.INPUT")) {
                     psError(psErrorCodeLast(), false, "failure in psphotReadout for chip %d, cell %d, readout %d\n", view->chip, view->cell, view->readout);
                     psFree (view);
Index: trunk/psphot/src/psphotImageQuality.c
===================================================================
--- trunk/psphot/src/psphotImageQuality.c	(revision 29902)
+++ trunk/psphot/src/psphotImageQuality.c	(revision 29936)
@@ -13,6 +13,5 @@
     psAssert (recipe, "missing recipe?");
 
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // skip the chisq image (optionally?)
@@ -99,9 +98,9 @@
         // r^2 sin(2t) = r^2 2 cos t sin t = 2 x y
 
-        // r^2 cos(3t) = r^2 cos^3 t - r^2 3 cos t sin^2 t = (x^3 - 3 x y^2) / r
-        // r^2 sin(3t) = r^2 3 cos^2 t sin t - sin^3 t = (3 x^2 y - y^3) / r
-
-        // r^2 cos(4t) = r^2 cos^4 t - r^2 6 cos^2 t sin^2 t + r^2 sin^4 t = (x^4 - 6 x^2 y^2 + y^4) / r^2
-        // r^2 sin(4t) = r^2 4 cos^3 t sin t - 4 sin^3 t cos t = (4 x^3 y - 4 y^3 x) / r^2
+        // r^3 cos(3t) = r^3 cos^3 t - r^2 3 cos t sin^2 t = (x^3 - 3 x y^2)
+        // r^3 sin(3t) = r^3 3 cos^2 t sin t - sin^3 t = (3 x^2 y - y^3)
+
+        // r^4 cos(4t) = r^4 cos^4 t - r^2 6 cos^2 t sin^2 t + r^2 sin^4 t = (x^4 - 6 x^2 y^2 + y^4)
+        // r^4 sin(4t) = r^4 4 cos^3 t sin t - 4 sin^3 t cos t = (4 x^3 y - 4 y^3 x)
 
         num++;
Index: trunk/psphot/src/psphotLoadPSF.c
===================================================================
--- trunk/psphot/src/psphotLoadPSF.c	(revision 29902)
+++ trunk/psphot/src/psphotLoadPSF.c	(revision 29936)
@@ -6,5 +6,5 @@
 
 // XXX for now (2010.01.27), the supporting programs do not define multiple PSPHOT.PSF.LOAD
-// files to go with multiple PSPHOT.INPUT files.  as a result, the implementation below is
+// files to go with multiple input files.  as a result, the implementation below is
 // currently going to work for the case of a single input file, but will fail if we try with a
 // stack of images.
@@ -59,11 +59,8 @@
 }
 
-bool psphotLoadPSF (pmConfig *config, const pmFPAview *view) {
+// PSPHOT.PSF.LOAD vs input file -- see note at top
+bool psphotLoadPSF (pmConfig *config, const pmFPAview *view, const char *filerule) {
 
-    bool status = false;
-
-    // XXX PSPHOT.PSF.LOAD vs PSPHOT.INPUT -- see note at top
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // loop over the available readouts
@@ -71,5 +68,5 @@
 
         // Generate the mask and weight images, including the user-defined analysis region of interest
-        if (!psphotLoadPSFReadout (config, view, "PSPHOT.INPUT", "PSPHOT.PSF.LOAD", i)) {
+        if (!psphotLoadPSFReadout (config, view, filerule, "PSPHOT.PSF.LOAD", i)) {
             psError (PSPHOT_ERR_CONFIG, false, "failed to load PSF model for PSPHOT.PSF.LOAD entry %d", i);
             return false;
Index: trunk/psphot/src/psphotMagnitudes.c
===================================================================
--- trunk/psphot/src/psphotMagnitudes.c	(revision 29902)
+++ trunk/psphot/src/psphotMagnitudes.c	(revision 29936)
@@ -9,6 +9,5 @@
     psAssert (recipe, "missing recipe?");
 
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // skip the chisq image (optionally?)
Index: trunk/psphot/src/psphotMakePSFImageLoop.c
===================================================================
--- trunk/psphot/src/psphotMakePSFImageLoop.c	(revision 29902)
+++ trunk/psphot/src/psphotMakePSFImageLoop.c	(revision 29936)
@@ -84,5 +84,5 @@
 
                 // run the actual photometry analysis on this chip/cell/readout
-                if (!psphotMakePSFReadout (config, view)) {
+                if (!psphotMakePSFReadout (config, view, "PSPHOT.INPUT")) {
                     psError(psErrorCodeLast(), false, "failure in psphotReadout for chip %d, cell %d, readout %d\n", view->chip, view->cell, view->readout);
                     psFree (view);
Index: trunk/psphot/src/psphotMakePSFReadout.c
===================================================================
--- trunk/psphot/src/psphotMakePSFReadout.c	(revision 29902)
+++ trunk/psphot/src/psphotMakePSFReadout.c	(revision 29936)
@@ -1,5 +1,5 @@
 # include "psphotInternal.h"
 
-bool psphotMakePSFReadout(pmConfig *config, const pmFPAview *view) {
+bool psphotMakePSFReadout(pmConfig *config, const pmFPAview *view, const char *filerule) {
 
     // measure the total elapsed time in psphotReadout.  XXX the current threading plan
@@ -19,5 +19,5 @@
 
     // set the photcode for this image
-    if (!psphotAddPhotcode (config, view, "PSPHOT.INPUT")) {
+    if (!psphotAddPhotcode (config, view, filerule)) {
         psError (PSPHOT_ERR_CONFIG, false, "trouble defining the photcode");
         return false;
@@ -29,21 +29,21 @@
 
     // Generate the mask and weight images, including the user-defined analysis region of interest
-    psphotSetMaskAndVariance (config, view, "PSPHOT.INPUT");
+    psphotSetMaskAndVariance (config, view, filerule);
     if (!strcasecmp (breakPt, "NOTHING")) {
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
     // generate a background model (median, smoothed image)
-    if (!psphotModelBackground (config, view, "PSPHOT.INPUT")) {
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+    if (!psphotModelBackground (config, view, filerule)) {
+        return psphotReadoutCleanup (config, view, filerule);
     }
-    if (!psphotSubtractBackground (config, view, "PSPHOT.INPUT")) {
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+    if (!psphotSubtractBackground (config, view, filerule)) {
+        return psphotReadoutCleanup (config, view, filerule);
     }
     if (!strcasecmp (breakPt, "BACKMDL")) {
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
-    psphotLoadExtSources (config, view, "PSPHOT.INPUT");
+    psphotLoadExtSources (config, view, filerule);
 
     // If sources have been supplied, then these should be used to measure the PSF include
@@ -53,24 +53,24 @@
     // a text file have no valid SN values, but psphotChoosePSF needs to select the top
     // PSF_MAX_NSTARS to generate the PSF.
-    if (!psphotCheckExtSources (config, view, "PSPHOT.INPUT")) {
+    if (!psphotCheckExtSources (config, view, filerule)) {
 	psLogMsg ("psphot", 3, "failure to select possible PSF sources (external or internal)");
-	return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+	return psphotReadoutCleanup (config, view, filerule);
     }
 
     // Use bright stellar objects to measure PSF. If we do not have enough stars to generate
     // the PSF, build one from the SEEING guess and model class
-    if (!psphotChoosePSF (config, view, "PSPHOT.INPUT")) {
+    if (!psphotChoosePSF (config, view, filerule)) {
 	psLogMsg ("psphot", 3, "failure to construct a psf model");
-	return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+	return psphotReadoutCleanup (config, view, filerule);
     }
 
     // measure aperture photometry corrections
 # if 0
-    if (!psphotApResid (config, view, "PSPHOT.INPUT")) {
+    if (!psphotApResid (config, view, filerule)) {
         psLogMsg ("psphot", 3, "failed on psphotApResid");
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 # endif
 
-    return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+    return psphotReadoutCleanup (config, view, filerule);
 }
Index: trunk/psphot/src/psphotMaskReadout.c
===================================================================
--- trunk/psphot/src/psphotMaskReadout.c	(revision 29902)
+++ trunk/psphot/src/psphotMaskReadout.c	(revision 29936)
@@ -9,6 +9,5 @@
     psAssert (recipe, "missing recipe?");
 
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // loop over the available readouts
@@ -17,5 +16,5 @@
 	// Generate the mask and weight images, including the user-defined analysis region of interest
 	if (!psphotSetMaskAndVarianceReadout (config, view, filerule, i, recipe)) {
-            psError (PSPHOT_ERR_CONFIG, false, "failed to generate mask for PSPHOT.INPUT entry %d", i);
+	    psError (PSPHOT_ERR_CONFIG, false, "failed to generate mask for %s entry %d", filerule, i);
 	    return false;
 	}
Index: trunk/psphot/src/psphotMergeSources.c
===================================================================
--- trunk/psphot/src/psphotMergeSources.c	(revision 29902)
+++ trunk/psphot/src/psphotMergeSources.c	(revision 29936)
@@ -8,8 +8,5 @@
 bool psphotMergeSources (pmConfig *config, const pmFPAview *view, const char *filerule)
 {
-    bool status = true;
-
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // loop over the available readouts
@@ -70,5 +67,5 @@
 // XXX This function needs to be updated to loop over set of input files.  At the moment, we
 // only expect a single entry for PSPHOT.INPUT.CMF and PSPHOT.SOURCES.TEXT, so we can only
-// associate input sources with a single entry for PSPHOT.INPUT
+// associate input sources with a single entry for the filerule
 bool psphotLoadExtSources (pmConfig *config, const pmFPAview *view, const char *filerule) {
 
@@ -166,4 +163,69 @@
 }
 
+// copy the known sources (as external) to the detection list of the given filerule
+bool psphotAddKnownSources (pmConfig *config, const pmFPAview *view, const char *filerule, psArray *inSources) {
+
+    bool status = false;
+
+    // select the appropriate recipe information
+    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSPHOT_RECIPE);
+    psAssert (recipe, "missing recipe?");
+
+    // determine properties (sky, moments) of initial sources
+    float OUTER = psMetadataLookupF32 (&status, recipe, "SKY_OUTER_RADIUS");
+    psAssert (status, "missing SKY_OUTER_RADIUS in recipe?");
+
+    // XXX this seems like an arbitrary number...
+    OUTER = PS_MAX(OUTER, 20.0); // XXX Guarantee that we can encompass the max moments radius
+
+    // find the currently selected readout 
+    pmFPAfile *file = pmFPAfileSelectSingle(config->files, filerule, 0); // File of interest
+    psAssert (file, "missing file?");
+
+    pmReadout *readout = pmFPAviewThisReadout(view, file->fpa);
+    psAssert (readout, "missing readout?");
+
+    pmDetections *detections = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.DETECTIONS");
+    if (!detections) {
+	detections = pmDetectionsAlloc();
+	detections->newSources = psArrayAllocEmpty (100);
+	// save detections on the readout->analysis
+	if (!psMetadataAddPtr (readout->analysis, PS_LIST_TAIL, "PSPHOT.DETECTIONS", PS_META_REPLACE | PS_DATA_UNKNOWN, "psphot detections", detections)) {
+	    psError (PSPHOT_ERR_CONFIG, false, "problem saving detections on readout");
+	    return false;
+	}
+    } else {
+	psMemIncrRefCounter(detections); // so we can free the detections below
+    }
+
+    // copy the sources from inSources to the new detection structure
+    for (int i = 0; i < inSources->n; i++) {
+      pmSource *inSource = inSources->data[i];
+
+      pmSource *newSource = pmSourceCopy(inSource);
+      newSource->mode |= PM_SOURCE_MODE_EXTERNAL;
+      
+      // drop the loaded source modelPSF
+      psFree (newSource->modelPSF);
+      // source->modelPSF = NULL;  check this!
+
+      // drop the references to the original image pixels:
+      pmSourceFreePixels (newSource);
+
+      // allocate image, weight, mask for the new image for each peak (square of radius OUTER)
+      pmSourceDefinePixels (newSource, readout, newSource->peak->x, newSource->peak->y, OUTER);
+
+      newSource->imageID = 0;
+      // XXX reset the source ID? raised questions about the meaning of this ID...
+      // P_PM_SOURCE_SET_ID(source, i);
+
+      psArrayAdd (detections->newSources, 100, newSource);
+    }
+    psLogMsg ("psphot", 3, "%ld known sources supplied", detections->newSources->n);
+
+    psFree (detections);
+    return true;
+}
+
 // extract the input sources corresponding to this readout
 // XXX this function needs to be updated to work with the new context of psphot inputs
@@ -405,6 +467,5 @@
     bool status = true;
 
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, ruleSrc);
 
     // skip the chisq image because it is a duplicate of the detection version
Index: trunk/psphot/src/psphotModelBackground.c
===================================================================
--- trunk/psphot/src/psphotModelBackground.c	(revision 29902)
+++ trunk/psphot/src/psphotModelBackground.c	(revision 29936)
@@ -383,16 +383,12 @@
 }
 
-// XXX supply filename or keep PSPHOT.INPUT fixed?
 bool psphotModelBackground (pmConfig *config, const pmFPAview *view, const char *filerule)
 {
-    bool status = false;
-
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // loop over the available readouts
     for (int i = 0; i < num; i++) {
         if (!psphotModelBackgroundReadoutFileIndex(config, view, filerule, i)) {
-            psError (PSPHOT_ERR_CONFIG, false, "failed to model background for PSPHOT.INPUT entry %d", i);
+	    psError (PSPHOT_ERR_CONFIG, false, "failed to model background for %s entry %d", filerule, i);
             return false;
         }
Index: trunk/psphot/src/psphotOutput.c
===================================================================
--- trunk/psphot/src/psphotOutput.c	(revision 29902)
+++ trunk/psphot/src/psphotOutput.c	(revision 29936)
@@ -1,3 +1,19 @@
 # include "psphotInternal.h"
+
+// convert filerule to filerule.NUM and look up in the config->arguments metadata
+int psphotFileruleCount(const pmConfig *config, const char *filerule) {
+
+    bool status = false;
+
+    psString name = NULL;
+    psStringAppend(&name, "%s.NUM", filerule);
+    int num = psMetadataLookupS32 (&status, config->arguments, name);
+    if (!status) {
+      // we only explicitly define (filerule.NUM) if we have more than 1
+      num = 1;
+    }
+    psFree (name);
+    return num;
+}
 
 pmReadout *psphotSelectBackground (pmConfig *config,
@@ -63,15 +79,4 @@
     fclose (f);
     return true;
-}
-
-// XXX replace this with a call to a pmConfig function (pmConfigDump...)
-bool psphotDumpConfig (pmConfig *config) {
-
-  psMetadataConfigWrite (config->user, "user.md");
-  psMetadataConfigWrite (config->camera, "camera.md");
-  psMetadataConfigWrite (config->recipes, "recipes.md");
-  psMetadataConfigWrite (config->arguments, "arguments.md");
-  psMetadataConfigWrite (config->files, "files.md");
-  return true;
 }
 
@@ -126,15 +131,42 @@
 }
 
+bool psphotCleanInputsReadout (pmConfig *config, const pmFPAview *view, const char *filerule, int index);
+bool psphotCleanInputs (pmConfig *config, const pmFPAview *view, const char *filerule) {
+
+    int num = psphotFileruleCount(config, filerule);
+
+    // loop over the available readouts
+    for (int i = 0; i < num; i++) {
+	if (!psphotCleanInputsReadout (config, view, filerule, i)) {
+	  psError (PSPHOT_ERR_CONFIG, false, "failed to clean inputs for %s entry %d", filerule, i);
+	    return false;
+	}
+    }
+    return true;
+}
+
+bool psphotCleanInputsReadout (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);
+
+    // XXX anything else to remove?
+    if (psMetadataLookup(readout->analysis, "PSPHOT.DETECTIONS")) {
+	psMetadataRemoveKey(readout->analysis, "PSPHOT.DETECTIONS");
+    }
+
+    return true;
+}
+
 bool psphotAddPhotcode (pmConfig *config, const pmFPAview *view, const char *filerule) {
 
-    bool status = false;
-
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // loop over the available readouts
     for (int i = 0; i < num; i++) {
 	if (!psphotAddPhotcodeReadout (config, view, filerule, i)) {
-            psError (PSPHOT_ERR_CONFIG, false, "failed to add photcode to PSPHOT.INPUT entry %d", i);
+	  psError (PSPHOT_ERR_CONFIG, false, "failed to add photcode to %s entry %d", filerule, i);
 	    return false;
 	}
@@ -357,5 +389,5 @@
     // XXX need alternative output function
     // psMetadata *psfData = pmPSFtoMetadata (NULL, try->psf);
-    // psMetadataConfigWrite (psfData, "psfmodel.dat");
+    // psMetadataConfigWrite (psfData, "psfmodel.dat", NULL);
     psLogMsg ("psphot.choosePSF", PS_LOG_INFO, "wrote out psf-subtracted image, psf data, exiting\n");
 
Index: trunk/psphot/src/psphotParseCamera.c
===================================================================
--- trunk/psphot/src/psphotParseCamera.c	(revision 29902)
+++ trunk/psphot/src/psphotParseCamera.c	(revision 29936)
@@ -39,6 +39,4 @@
         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: trunk/psphot/src/psphotRadialApertures.c
===================================================================
--- trunk/psphot/src/psphotRadialApertures.c	(revision 29902)
+++ trunk/psphot/src/psphotRadialApertures.c	(revision 29936)
@@ -18,6 +18,5 @@
     }
 
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // loop over the available readouts
Index: trunk/psphot/src/psphotRadialAperturesByObject.c
===================================================================
--- trunk/psphot/src/psphotRadialAperturesByObject.c	(revision 29902)
+++ trunk/psphot/src/psphotRadialAperturesByObject.c	(revision 29936)
@@ -21,6 +21,5 @@
 
     // number of images used to define sources
-    int nImages = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int nImages = psphotFileruleCount(config, filerule);
 
     // radMax stores the upper bounds of the annuli
Index: trunk/psphot/src/psphotRadiusChecks.c
===================================================================
--- trunk/psphot/src/psphotRadiusChecks.c	(revision 29902)
+++ trunk/psphot/src/psphotRadiusChecks.c	(revision 29936)
@@ -135,8 +135,5 @@
     EXT_FIT_MAX_RADIUS = psMetadataLookupF32 (&status, recipe, "EXT_FIT_MAX_RADIUS");
 
-    float skyMean  = psMetadataLookupF32 (&status, readout->analysis, "SKY_MEAN");
     float skyStdev = psMetadataLookupF32 (&status, readout->analysis, "SKY_STDEV");
-
-    fprintf (stderr, "sky: %f +/- %f\n", skyMean, skyStdev);
 
     EXT_FIT_SKY_SIG = skyStdev;
Index: trunk/psphot/src/psphotReadout.c
===================================================================
--- trunk/psphot/src/psphotReadout.c	(revision 29902)
+++ trunk/psphot/src/psphotReadout.c	(revision 29936)
@@ -9,5 +9,5 @@
 }
 
-bool psphotReadout(pmConfig *config, const pmFPAview *view) {
+bool psphotReadout(pmConfig *config, const pmFPAview *view, const char *filerule) {
 
     // measure the total elapsed time in psphotReadout.  dtime is the elapsed time used jointly
@@ -27,6 +27,12 @@
     psAssert (breakPt, "configuration error: set BREAK_POINT");
 
+    // remove cruft from the input analysis structure
+    if (!psphotCleanInputs (config, view, filerule)) {
+        psError (PSPHOT_ERR_PROG, false, "trouble setting up the inputs");
+        return false;
+    }
+
     // set the photcode for this image
-    if (!psphotAddPhotcode (config, view, "PSPHOT.INPUT")) {
+    if (!psphotAddPhotcode (config, view, filerule)) {
         psError (PSPHOT_ERR_CONFIG, false, "trouble defining the photcode");
         return false;
@@ -34,102 +40,101 @@
 
     // Generate the mask and weight images, including the user-defined analysis region of interest
-    if (!psphotSetMaskAndVariance (config, view, "PSPHOT.INPUT")) {
-        return psphotReadoutCleanup(config, view, "PSPHOT.INPUT");
+    if (!psphotSetMaskAndVariance (config, view, filerule)) {
+        return psphotReadoutCleanup(config, view, filerule);
     }
     if (!strcasecmp (breakPt, "NOTHING")) {
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
     // generate a background model (median, smoothed image)
-    if (!psphotModelBackground (config, view, "PSPHOT.INPUT")) {
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
-    }
-
-    if (!psphotSubtractBackground (config, view, "PSPHOT.INPUT")) {
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+    if (!psphotModelBackground (config, view, filerule)) {
+        return psphotReadoutCleanup (config, view, filerule);
+    }
+
+    if (!psphotSubtractBackground (config, view, filerule)) {
+        return psphotReadoutCleanup (config, view, filerule);
     }
     if (!strcasecmp (breakPt, "BACKMDL")) {
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
     // load the psf model, if suppled.  FWHM_MAJ,FWHM_MIN,etc are determined and saved on
-    // readout->analysis. XXX Note: this function currently only works with a single
-    // PSPHOT.INPUT
-    if (!psphotLoadPSF (config, view)) { // ??? need to supply 2 ?
+    // readout->analysis. NOTE: this function currently only loads from PSPHOT.PSF.LOAD
+    if (!psphotLoadPSF (config, view, filerule)) { // ??? need to supply 2 ?
         psError (PSPHOT_ERR_UNKNOWN, false, "error loading psf model");
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
     // find the detections (by peak and/or footprint) in the image.
-    if (!psphotFindDetections (config, view, "PSPHOT.INPUT", true)) { // pass 1
+    if (!psphotFindDetections (config, view, filerule, true)) { // pass 1
         // this only happens if we had an error in psphotFindDetections
         psError (PSPHOT_ERR_UNKNOWN, false, "failure in peak analysis");
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
     // construct sources and measure basic stats (saved on detections->newSources)
-    if (!psphotSourceStats (config, view, "PSPHOT.INPUT", true)) { // pass 1
+    if (!psphotSourceStats (config, view, filerule, true)) { // pass 1
         psError(PSPHOT_ERR_UNKNOWN, false, "failure to generate sources");
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
     if (!strcasecmp (breakPt, "PEAKS")) {
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
     // find blended neighbors of very saturated stars (detections->newSources)
-    if (!psphotDeblendSatstars (config, view, "PSPHOT.INPUT")) {
+    if (!psphotDeblendSatstars (config, view, filerule)) {
         psError (PSPHOT_ERR_UNKNOWN, false, "failed on satstar deblend analysis");
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
     // mark blended peaks PS_SOURCE_BLEND (detections->newSources)
-    if (!psphotBasicDeblend (config, view, "PSPHOT.INPUT")) {
+    if (!psphotBasicDeblend (config, view, filerule)) {
         psError (PSPHOT_ERR_UNKNOWN, false, "failed on deblend analysis");
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
     // classify sources based on moments, brightness.  if a PSF model has been loaded, the PSF
     // clump defined for it is used not measured (detections->newSources)
-    if (!psphotRoughClass (config, view, "PSPHOT.INPUT")) { // pass 1
+    if (!psphotRoughClass (config, view, filerule)) { // pass 1
         psError (PSPHOT_ERR_UNKNOWN, false, "failed to determine rough classifications");
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
     // if we were not supplied a PSF model, determine the IQ stats here (detections->newSources)
-    if (!psphotImageQuality (config, view, "PSPHOT.INPUT")) { // pass 1
+    if (!psphotImageQuality (config, view, filerule)) { // pass 1
         psError (PSPHOT_ERR_UNKNOWN, false, "failed to measure image quality");
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
     if (!strcasecmp (breakPt, "MOMENTS")) {
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
     // use bright stellar objects to measure PSF if we were supplied a PSF for any input file,
     // this step is skipped
-    if (!psphotChoosePSF (config, view, "PSPHOT.INPUT")) { // pass 1
+    if (!psphotChoosePSF (config, view, filerule)) { // pass 1
         psLogMsg ("psphot", 3, "failure to construct a psf model");
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
     if (!strcasecmp (breakPt, "PSFMODEL")) {
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
     // include externally-supplied sources
     // XXX fix this in the new multi-input context
-    // psphotLoadExtSources (config, view, "PSPHOT.INPUT"); // pass 1
+    // psphotLoadExtSources (config, view, filerule); // pass 1
 
     // construct an initial model for each object, set the radius to fitRadius, set circular
     // fit mask (detections->newSources)
-    psphotGuessModels (config, view, "PSPHOT.INPUT"); // pass 1
+    psphotGuessModels (config, view, filerule); // pass 1
 
     // merge the newly selected sources into the existing list
     // NOTE: merge OLD and NEW
-    psphotMergeSources (config, view, "PSPHOT.INPUT");
+    psphotMergeSources (config, view, filerule);
 
     // linear PSF fit to source peaks, subtract the models from the image (in PSF mask)
-    psphotFitSourcesLinear (config, view, "PSPHOT.INPUT", false); // pass 1 (detections->allSources)
+    psphotFitSourcesLinear (config, view, filerule, false); // pass 1 (detections->allSources)
 
     // identify CRs and extended sources (only unmeasured sources are measured)
-    psphotSourceSize (config, view, "PSPHOT.INPUT", true); // pass 1 (detections->allSources)
+    psphotSourceSize (config, view, filerule, true); // pass 1 (detections->allSources)
     if (!strcasecmp (breakPt, "ENSEMBLE")) {
         goto finish;
@@ -138,12 +143,12 @@
     // non-linear PSF and EXT fit to brighter sources
     // replace model flux, adjust mask as needed, fit, subtract the models (full stamp)
-    psphotBlendFit (config, view, "PSPHOT.INPUT"); // pass 1 (detections->allSources)
+    psphotBlendFit (config, view, filerule); // pass 1 (detections->allSources)
 
     // replace all sources
-    psphotReplaceAllSources (config, view, "PSPHOT.INPUT"); // pass 1 (detections->allSources)
+    psphotReplaceAllSources (config, view, filerule); // pass 1 (detections->allSources)
 
     // linear fit to include all sources (subtract again)
     // NOTE : apply to ALL sources (extended + psf)
-    psphotFitSourcesLinear (config, view, "PSPHOT.INPUT", true); // pass 2 (detections->allSources)
+    psphotFitSourcesLinear (config, view, filerule, true); // pass 2 (detections->allSources)
 
     // if we only do one pass, skip to extended source analysis
@@ -153,40 +158,40 @@
 
     // add noise for subtracted objects
-    psphotAddNoise (config, view, "PSPHOT.INPUT"); // pass 1 (detections->allSources)
+    psphotAddNoise (config, view, filerule); // pass 1 (detections->allSources)
 
     // find fainter sources
     // NOTE: finds new peaks and new footprints, OLD and FULL set are saved on detections
-    psphotFindDetections (config, view, "PSPHOT.INPUT", false); // pass 2 (detections->peaks, detections->footprints)
+    psphotFindDetections (config, view, filerule, false); // pass 2 (detections->peaks, detections->footprints)
 
     // remove noise for subtracted objects (ie, return to normal noise level)
     // NOTE: this needs to operate only on the OLD sources
-    psphotSubNoise (config, view, "PSPHOT.INPUT"); // pass 1 (detections->allSources)
+    psphotSubNoise (config, view, filerule); // pass 1 (detections->allSources)
 
     // define new sources based on only the new peaks
     // NOTE: new sources are saved on detections->newSources
-    psphotSourceStats (config, view, "PSPHOT.INPUT", false); // pass 2 (detections->newSources)
+    psphotSourceStats (config, view, filerule, false); // pass 2 (detections->newSources)
 
     // set source type
     // NOTE: apply only to detections->newSources
-    if (!psphotRoughClass (config, view, "PSPHOT.INPUT")) { // pass 2 (detections->newSources)
+    if (!psphotRoughClass (config, view, filerule)) { // pass 2 (detections->newSources)
         psLogMsg ("psphot", 3, "failed to find a valid PSF clump for image");
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
     // create full input models, set the radius to fitRadius, set circular fit mask
     // NOTE: apply only to detections->newSources
-    psphotGuessModels (config, view, "PSPHOT.INPUT"); // pass 2 (detections->newSources)
+    psphotGuessModels (config, view, filerule); // pass 2 (detections->newSources)
 
     // replace all sources so fit below applies to all at once
     // NOTE: apply only to OLD sources (which have been subtracted)
-    psphotReplaceAllSources (config, view, "PSPHOT.INPUT"); // pass 2
+    psphotReplaceAllSources (config, view, filerule); // pass 2
 
     // merge the newly selected sources into the existing list
     // NOTE: merge OLD and NEW
     // XXX check on free of sources...
-    psphotMergeSources (config, view, "PSPHOT.INPUT"); // (detections->newSources + detections->allSources -> detections->allSources)
+    psphotMergeSources (config, view, filerule); // (detections->newSources + detections->allSources -> detections->allSources)
 
     // NOTE: apply to ALL sources
-    psphotFitSourcesLinear (config, view, "PSPHOT.INPUT", true); // pass 3 (detections->allSources)
+    psphotFitSourcesLinear (config, view, filerule, true); // pass 3 (detections->allSources)
 
 pass1finish:
@@ -194,8 +199,8 @@
     // measure source size for the remaining sources
     // NOTE: applies only to NEW (unmeasured) sources
-    psphotSourceSize (config, view, "PSPHOT.INPUT", false); // pass 2 (detections->allSources)
-
-    psphotExtendedSourceAnalysis (config, view, "PSPHOT.INPUT"); // pass 1 (detections->allSources)
-    psphotExtendedSourceFits (config, view, "PSPHOT.INPUT"); // pass 1 (detections->allSources)
+    psphotSourceSize (config, view, filerule, false); // pass 2 (detections->allSources)
+
+    psphotExtendedSourceAnalysis (config, view, filerule); // pass 1 (detections->allSources)
+    psphotExtendedSourceFits (config, view, filerule); // pass 1 (detections->allSources)
 
 finish:
@@ -205,15 +210,15 @@
 
     // measure aperture photometry corrections
-    if (!psphotApResid (config, view, "PSPHOT.INPUT")) { // pass 1 (detections->allSources)
+    if (!psphotApResid (config, view, filerule)) { // pass 1 (detections->allSources)
         psLogMsg ("psphot", 3, "failed on psphotApResid");
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
     // calculate source magnitudes
-    if (!psphotMagnitudes(config, view, "PSPHOT.INPUT")) { // pass 1 (detections->allSources)
+    if (!psphotMagnitudes(config, view, filerule)) { // pass 1 (detections->allSources)
       psErrorStackPrint(stderr, "Unable to do magnitudes.");
         psErrorClear();
     }
-    if (!psphotEfficiency(config, view, "PSPHOT.INPUT")) { // pass 1
+    if (!psphotEfficiency(config, view, filerule)) { // pass 1
         psErrorStackPrint(stderr, "Unable to determine detection efficiencies from fake sources");
         psErrorClear();
@@ -224,20 +229,20 @@
 
     // replace background in residual image
-    if (!psphotSkyReplace (config, view, "PSPHOT.INPUT")) { // pass 1
+    if (!psphotSkyReplace (config, view, filerule)) { // pass 1
       psErrorStackPrint(stderr, "Unable to replace sky");
       psErrorClear();
 
 /*       psLogMsg("psphot", 3, "failed on psphotSkyReplace"); */
-/*       return(psphotReadoutCleanup(config, view, "PSPHOT.INPUT")); */
+/*       return(psphotReadoutCleanup(config, view, filerule)); */
     }
     // drop the references to the image pixels held by each source
-    if (!psphotSourceFreePixels (config, view, "PSPHOT.INPUT")) { // pass 1
+    if (!psphotSourceFreePixels (config, view, filerule)) { // pass 1
       psErrorStackPrint(stderr, "Unable to free source pixels");
       psErrorClear();
 
 /*       psLogMsg ("psphot", 3, "failed on psphotSourceFreePixels"); */
-/*       return(psphotReadoutCleanup(config, view, "PSPHOT.INPUT")); */
+/*       return(psphotReadoutCleanup(config, view, filerule)); */
     }
     // create the exported-metadata and free local data
-    return psphotReadoutCleanup(config, view, "PSPHOT.INPUT");
+    return psphotReadoutCleanup(config, view, filerule);
 }
Index: trunk/psphot/src/psphotReadoutCleanup.c
===================================================================
--- trunk/psphot/src/psphotReadoutCleanup.c	(revision 29902)
+++ trunk/psphot/src/psphotReadoutCleanup.c	(revision 29936)
@@ -19,6 +19,5 @@
     psAssert (recipe, "missing recipe?");
 
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // loop over the available readouts
Index: trunk/psphot/src/psphotReadoutFindPSF.c
===================================================================
--- trunk/psphot/src/psphotReadoutFindPSF.c	(revision 29902)
+++ trunk/psphot/src/psphotReadoutFindPSF.c	(revision 29936)
@@ -3,10 +3,10 @@
 // in this psphotReadout-variant, we are only trying to determine the PSF given an existing set
 // of input source positions to use as initial PSF stars.
-bool psphotReadoutFindPSF(pmConfig *config, const pmFPAview *view, psArray *inSources) {
+bool psphotReadoutFindPSF(pmConfig *config, const pmFPAview *view, const char *filerule, psArray *inSources) {
 
     psTimerStart ("psphotReadout");
 
-    // set the photcode for the PSPHOT.INPUT
-    if (!psphotAddPhotcode(config, view, "PSPHOT.INPUT")) {
+    // set the photcode for the input
+    if (!psphotAddPhotcode(config, view, filerule)) {
         psError(PSPHOT_ERR_CONFIG, false, "trouble defining the photcode");
         return false;
@@ -14,18 +14,18 @@
 
     // Generate the mask and variance images, including the user-defined analysis region of interest
-    psphotSetMaskAndVariance (config, view, "PSPHOT.INPUT");
+    psphotSetMaskAndVariance (config, view, filerule);
 
     // Note that in this implementation, we do NOT model the background and we do not
     // attempt to detect the sources in the image
 
-    // include externally-supplied sources (supplied as PSPHOT.INPUT.CMF)
-    // XXX we assume a single set of input sources is supplied
-    if (!psphotDetectionsFromSources (config, view, "PSPHOT.INPUT", inSources)) {
+    // include the externally-supplied sources (inSources)
+    // (we assume a single set of input sources is supplied)
+    if (!psphotDetectionsFromSources (config, view, filerule, inSources)) {
         psError(PSPHOT_ERR_ARGUMENTS, true, "Can't find PSF stars");
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
     // construct detections->newSources and measure basic stats (moments, local sky)
-    if (!psphotSourceStats(config, view, "PSPHOT.INPUT", true)) {
+    if (!psphotSourceStats(config, view, filerule, true)) {
         psError(PSPHOT_ERR_UNKNOWN, false, "failure to generate sources");
 	return false;
@@ -33,5 +33,5 @@
 
     // peak flux is wrong : use the peak measured in the moments analysis:
-    if (!psphotRepairLoadedSources(config, view, "PSPHOT.INPUT")) {
+    if (!psphotRepairLoadedSources(config, view, filerule)) {
         psError(PSPHOT_ERR_UNKNOWN, false, "failure to repair sources");
 	return false;
@@ -39,17 +39,17 @@
 
     // classify sources based on moments, brightness (psf is not known)
-    if (!psphotRoughClass (config, view, "PSPHOT.INPUT")) {
+    if (!psphotRoughClass (config, view, filerule)) {
         psError (PSPHOT_ERR_UNKNOWN, false, "failed to determine rough source class");
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
-    if (!psphotImageQuality (config, view, "PSPHOT.INPUT")) {
+    if (!psphotImageQuality (config, view, filerule)) {
         psError (PSPHOT_ERR_UNKNOWN, false, "failed to measure image quality");
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
-    if (!psphotChoosePSF(config, view, "PSPHOT.INPUT")) {
+    if (!psphotChoosePSF(config, view, filerule)) {
         psError(PSPHOT_ERR_PSF, false, "Failed to construct a psf model");
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
@@ -59,23 +59,23 @@
     // fits from that analysis, or run the linear PSF fit for all objects currently in hand
     // construct an initial model for each object, set the radius to fitRadius, set circular fit mask
-    psphotGuessModels (config, view, "PSPHOT.INPUT");
+    psphotGuessModels (config, view, filerule);
 # endif
 
     // merge the newly selected sources into the existing list
     // NOTE: merge OLD and NEW
-    psphotMergeSources (config, view, "PSPHOT.INPUT"); 
+    psphotMergeSources (config, view, filerule); 
 
 # if 0
     // measure aperture photometry corrections
-    if (!psphotApResid (config, view, "PSPHOT.INPUT")) {
+    if (!psphotApResid (config, view, filerule)) {
         psLogMsg ("psphot", 3, "failed on psphotApResid");
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 # endif
 
     // drop the references to the image pixels held by each source
-    psphotSourceFreePixels(config, view, "PSPHOT.INPUT");
+    psphotSourceFreePixels(config, view, filerule);
 
     // create the exported-metadata and free local data
-    return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+    return psphotReadoutCleanup (config, view, filerule);
 }
Index: trunk/psphot/src/psphotReadoutForcedKnownSources.c
===================================================================
--- trunk/psphot/src/psphotReadoutForcedKnownSources.c	(revision 29936)
+++ trunk/psphot/src/psphotReadoutForcedKnownSources.c	(revision 29936)
@@ -0,0 +1,56 @@
+# include "psphotInternal.h"
+
+// in this psphotReadout-variant, we are only measuring the photometry for known source
+// position, using a supplied PSF
+bool psphotReadoutForcedKnownSources(pmConfig *config, const pmFPAview *view, const char *filerule, psArray *inSources) {
+
+    psTimerStart ("psphotReadout");
+
+    // remove cruft from the input analysis structure
+    if (!psphotCleanInputs (config, view, filerule)) {
+        psError (PSPHOT_ERR_PROG, false, "trouble setting up the inputs");
+        return false;
+    }
+
+    // set the photcode for this image
+    if (!psphotAddPhotcode(config, view, filerule)) {
+        psError(PSPHOT_ERR_CONFIG, false, "trouble defining the photcode");
+        return false;
+    }
+
+    // Generate the mask and weight images, including the user-defined analysis region of interest
+    psphotSetMaskAndVariance (config, view, filerule);
+
+    // Note that in this implementation, we do NOT model the background and we do not
+    // attempt to detect the sources in the image
+
+    if (!psphotAddKnownSources (config, view, filerule, inSources)) {
+	psError(PSPHOT_ERR_UNKNOWN, false, "failure to load supplied sources");
+	return false;
+    }
+
+    if (!psphotLoadPSF (config, view, filerule)) {
+    	// this only happens if we had a programming error in psphotLoadPSF
+        psError (PSPHOT_ERR_UNKNOWN, false, "error loading psf model");
+        return psphotReadoutCleanup (config, view, filerule);
+    }
+
+    // construct an initial model for each object
+    psphotGuessModels (config, view, filerule);
+
+    // merge the newly selected sources into the existing list
+    // NOTE: merge OLD and NEW
+    psphotMergeSources (config, view, filerule); 
+
+    // linear PSF fit to source peaks
+    psphotFitSourcesLinear (config, view, filerule, false);
+
+    // calculate source magnitudes
+    psphotMagnitudes(config, view, filerule);
+
+    // drop the references to the image pixels held by each source
+    psphotSourceFreePixels (config, view, filerule);
+
+    // create the exported-metadata and free local data
+    return psphotReadoutCleanup (config, view, filerule);
+}
Index: trunk/psphot/src/psphotReadoutKnownSources.c
===================================================================
--- trunk/psphot/src/psphotReadoutKnownSources.c	(revision 29902)
+++ trunk/psphot/src/psphotReadoutKnownSources.c	(revision 29936)
@@ -3,10 +3,10 @@
 // in this psphotReadout-variant, we are only measuring the photometry for known sources, using
 // a PSF generated for this observation from those sources
-bool psphotReadoutKnownSources(pmConfig *config, const pmFPAview *view, psArray *inSources) {
+bool psphotReadoutKnownSources(pmConfig *config, const pmFPAview *view, const char *filerule, psArray *inSources) {
 
     psTimerStart ("psphotReadout");
 
     // set the photcode for this image
-    if (!psphotAddPhotcode(config, view, "PSPHOT.INPUT")) {
+    if (!psphotAddPhotcode(config, view, filerule)) {
         psError(PSPHOT_ERR_CONFIG, false, "trouble defining the photcode");
         return false;
@@ -14,5 +14,5 @@
 
     // Generate the mask and weight images, including the user-defined analysis region of interest
-    psphotSetMaskAndVariance (config, view, "PSPHOT.INPUT");
+    psphotSetMaskAndVariance (config, view, filerule);
 
     // Note that in this implementation, we do NOT model the background and we do not
@@ -20,11 +20,11 @@
 
     // include externally-supplied sources (supplied as PSPHOT.INPUT.CMF)
-    if (!psphotDetectionsFromSources (config, view, "PSPHOT.INPUT", inSources)) {
+    if (!psphotDetectionsFromSources (config, view, filerule, inSources)) {
         psError(PSPHOT_ERR_ARGUMENTS, true, "Can't find PSF stars");
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
     // construct sources and measure basic stats
-    if (!psphotSourceStats (config, view, "PSPHOT.INPUT", true)) {
+    if (!psphotSourceStats (config, view, filerule, true)) {
         psError(PSPHOT_ERR_UNKNOWN, false, "failure to generate sources");
 	return false;
@@ -32,5 +32,5 @@
 
     // peak flux is wrong : use the peak measured in the moments analysis:
-    if (!psphotRepairLoadedSources(config, view, "PSPHOT.INPUT")) {
+    if (!psphotRepairLoadedSources(config, view, filerule)) {
         psError(PSPHOT_ERR_UNKNOWN, false, "failure to repair sources");
 	return false;
@@ -38,37 +38,37 @@
 
     // classify sources based on moments, brightness (psf is not known)
-    if (!psphotRoughClass (config, view, "PSPHOT.INPUT")) {
+    if (!psphotRoughClass (config, view, filerule)) {
         psError (PSPHOT_ERR_UNKNOWN, false, "failed to determine rough source class");
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
-    if (!psphotChoosePSF (config, view, "PSPHOT.INPUT")) {
+    if (!psphotChoosePSF (config, view, filerule)) {
         psError(PSPHOT_ERR_PSF, false, "Failed to construct a psf model");
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
     // construct an initial model for each object
-    psphotGuessModels (config, view, "PSPHOT.INPUT");
+    psphotGuessModels (config, view, filerule);
 
     // merge the newly selected sources into the existing list
     // NOTE: merge OLD and NEW
-    psphotMergeSources (config, view, "PSPHOT.INPUT"); 
+    psphotMergeSources (config, view, filerule); 
 
     // linear PSF fit to source peaks
-    psphotFitSourcesLinear (config, view, "PSPHOT.INPUT", false);
+    psphotFitSourcesLinear (config, view, filerule, false);
 
     // measure aperture photometry corrections
-    if (!psphotApResid (config, view, "PSPHOT.INPUT")) {
+    if (!psphotApResid (config, view, filerule)) {
         psLogMsg ("psphot", 3, "failed on psphotApResid");
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
     // calculate source magnitudes
-    psphotMagnitudes(config, view, "PSPHOT.INPUT");
+    psphotMagnitudes(config, view, filerule);
 
     // drop the references to the image pixels held by each source
-    psphotSourceFreePixels (config, view, "PSPHOT.INPUT");
+    psphotSourceFreePixels (config, view, filerule);
 
     // create the exported-metadata and free local data
-    return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+    return psphotReadoutCleanup (config, view, filerule);
 }
Index: trunk/psphot/src/psphotReadoutMinimal.c
===================================================================
--- trunk/psphot/src/psphotReadoutMinimal.c	(revision 29902)
+++ trunk/psphot/src/psphotReadoutMinimal.c	(revision 29936)
@@ -7,5 +7,5 @@
 // NOTE: ppSub needs to perform extended source analysis for comets and trails.
 
-bool psphotReadoutMinimal(pmConfig *config, const pmFPAview *view) {
+bool psphotReadoutMinimal(pmConfig *config, const pmFPAview *view, const char *filerule) {
 
     // measure the total elapsed time in psphotReadout.  XXX the current threading plan
@@ -18,5 +18,5 @@
 
     // set the photcode for this image
-    if (!psphotAddPhotcode(config, view, "PSPHOT.INPUT")) {
+    if (!psphotAddPhotcode(config, view, filerule)) {
         psError(PSPHOT_ERR_CONFIG, false, "trouble defining the photcode");
         return false;
@@ -24,63 +24,63 @@
 
     // Generate the mask and weight images, including the user-defined analysis region of interest
-    psphotSetMaskAndVariance (config, view, "PSPHOT.INPUT");
+    psphotSetMaskAndVariance (config, view, filerule);
 
     // load the psf model, if suppled.  FWHM_X,FWHM_Y,etc are saved on readout->analysis
-    if (!psphotLoadPSF (config, view)) {
+    if (!psphotLoadPSF (config, view, filerule)) {
       psError (PSPHOT_ERR_CONFIG, false, "missing psf model");
-      return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+      return psphotReadoutCleanup (config, view, filerule);
     }
 
     // find the detections (by peak and/or footprint) in the image. (final pass)
-    if (!psphotFindDetections(config, view, "PSPHOT.INPUT", false)) {
+    if (!psphotFindDetections(config, view, filerule, false)) {
         psError (PSPHOT_ERR_UNKNOWN, false, "failure in peak analysis");
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
     // construct sources and measure basic stats (saved on detections->newSources)
-    if (!psphotSourceStats (config, view, "PSPHOT.INPUT", false)) { // pass 1
+    if (!psphotSourceStats (config, view, filerule, false)) { // pass 1
         psError(PSPHOT_ERR_UNKNOWN, false, "failure to generate sources");
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
     // find blended neighbors of very saturated stars
-    psphotDeblendSatstars (config, view, "PSPHOT.INPUT");
+    psphotDeblendSatstars (config, view, filerule);
 
     // mark blended peaks PS_SOURCE_BLEND
-    if (!psphotBasicDeblend (config, view, "PSPHOT.INPUT")) {
+    if (!psphotBasicDeblend (config, view, filerule)) {
         psLogMsg ("psphot", 3, "failed on deblend analysis");
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
     // classify sources based on moments, brightness (use supplied psf shape parameters)
-    if (!psphotRoughClass (config, view, "PSPHOT.INPUT")) {
+    if (!psphotRoughClass (config, view, filerule)) {
         psLogMsg ("psphot", 3, "failed to find a valid PSF clump for image");
-        return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+        return psphotReadoutCleanup (config, view, filerule);
     }
 
     // construct an initial model for each object
-    psphotGuessModels (config, view, "PSPHOT.INPUT");
+    psphotGuessModels (config, view, filerule);
 
     // merge the newly selected sources into the existing list
-    psphotMergeSources (config, view, "PSPHOT.INPUT");
+    psphotMergeSources (config, view, filerule);
 
     // linear PSF fit to source peaks
-    psphotFitSourcesLinear (config, view, "PSPHOT.INPUT", false);
+    psphotFitSourcesLinear (config, view, filerule, false);
 
 // XXX eventually, add the extended source fits here
 # if (0)
     // measure source size for the remaining sources
-    psphotSourceSize (config, view, "PSPHOT.INPUT");
+    psphotSourceSize (config, view, filerule);
 
-    psphotExtendedSourceAnalysis (config, view, "PSPHOT.INPUT");
+    psphotExtendedSourceAnalysis (config, view, filerule);
 
-    psphotExtendedSourceFits (config, view, "PSPHOT.INPUT");
+    psphotExtendedSourceFits (config, view, filerule);
 # endif
 
     // calculate source magnitudes
-    psphotMagnitudes(config, view, "PSPHOT.INPUT");
+    psphotMagnitudes(config, view, filerule);
 
     // XXX ensure this is measured if the analysis succeeds (even if quality is low)
-    if (!psphotEfficiency(config, view, "PSPHOT.INPUT")) {
+    if (!psphotEfficiency(config, view, filerule)) {
         psErrorStackPrint(stderr, "Unable to determine detection efficiencies from fake sources");
         psErrorClear();
@@ -88,7 +88,7 @@
 
     // drop the references to the image pixels held by each source
-    psphotSourceFreePixels (config, view, "PSPHOT.INPUT");
+    psphotSourceFreePixels (config, view, filerule);
 
     // create the exported-metadata and free local data
-    return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
+    return psphotReadoutCleanup (config, view, filerule);
 }
Index: trunk/psphot/src/psphotReplaceUnfit.c
===================================================================
--- trunk/psphot/src/psphotReplaceUnfit.c	(revision 29902)
+++ trunk/psphot/src/psphotReplaceUnfit.c	(revision 29936)
@@ -31,11 +31,10 @@
     psAssert (recipe, "missing recipe?");
 
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // loop over the available readouts
     for (int i = 0; i < num; i++) {
 	if (!psphotReplaceAllSourcesReadout (config, view, filerule, i, recipe)) {
-            psError (PSPHOT_ERR_CONFIG, false, "failed to replace all sources for PSPHOT.INPUT entry %d", i);
+	    psError (PSPHOT_ERR_CONFIG, false, "failed to replace all sources for %s entry %d", filerule, i);
 	    return false;
 	}
Index: trunk/psphot/src/psphotRoughClass.c
===================================================================
--- trunk/psphot/src/psphotRoughClass.c	(revision 29902)
+++ trunk/psphot/src/psphotRoughClass.c	(revision 29936)
@@ -16,6 +16,5 @@
     psAssert (recipe, "missing recipe?");
 
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // skip the chisq image (optionally?)
Index: trunk/psphot/src/psphotSkyReplace.c
===================================================================
--- trunk/psphot/src/psphotSkyReplace.c	(revision 29902)
+++ trunk/psphot/src/psphotSkyReplace.c	(revision 29936)
@@ -5,6 +5,5 @@
     bool status = true;
 
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // skip the chisq image (optionally?)
Index: trunk/psphot/src/psphotSourceFreePixels.c
===================================================================
--- trunk/psphot/src/psphotSourceFreePixels.c	(revision 29902)
+++ trunk/psphot/src/psphotSourceFreePixels.c	(revision 29936)
@@ -3,8 +3,5 @@
 bool psphotSourceFreePixels (pmConfig *config, const pmFPAview *view, const char *filerule)
 {
-    bool status = true;
-
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // loop over the available readouts
Index: trunk/psphot/src/psphotSourceMatch.c
===================================================================
--- trunk/psphot/src/psphotSourceMatch.c	(revision 29902)
+++ trunk/psphot/src/psphotSourceMatch.c	(revision 29936)
@@ -6,15 +6,12 @@
 psArray *psphotMatchSources (pmConfig *config, const pmFPAview *view, const char *filerule) 
 {
-    bool status = true;
-
     psArray *objects = psArrayAllocEmpty(100);
 
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // loop over the available readouts
     for (int i = 0; i < num; i++) {
         if (!psphotMatchSourcesReadout (objects, config, view, filerule, i)) {
-            psError (PSPHOT_ERR_CONFIG, false, "failed to merge sources for PSPHOT.INPUT entry %d", i);
+	    psError (PSPHOT_ERR_CONFIG, false, "failed to merge sources for %s entry %d", filerule, i);
 	    psFree (objects);
             return NULL;
@@ -162,6 +159,5 @@
     psAssert (status, "missing SKY_OUTER_RADIUS in recipe?");
 
-    int nImages = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int nImages = psphotFileruleCount(config, filerule);
 
     // generate look-up arrays for detections and readouts
Index: trunk/psphot/src/psphotSourceSize.c
===================================================================
--- trunk/psphot/src/psphotSourceSize.c	(revision 29902)
+++ trunk/psphot/src/psphotSourceSize.c	(revision 29936)
@@ -49,6 +49,5 @@
     psAssert (recipe, "missing recipe?");
 
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // skip the chisq image (optionally?)
@@ -60,5 +59,5 @@
         if (i == chisqNum) continue; // skip chisq image
         if (!psphotSourceSizeReadout (config, view, filerule, i, recipe, getPSFsize)) {
-            psError (PSPHOT_ERR_CONFIG, false, "failed on source size analysis for PSPHOT.INPUT entry %d", i);
+	    psError (PSPHOT_ERR_CONFIG, false, "failed on source size analysis for %s entry %d", filerule, i);
             return false;
         }
Index: trunk/psphot/src/psphotSourceStats.c
===================================================================
--- trunk/psphot/src/psphotSourceStats.c	(revision 29902)
+++ trunk/psphot/src/psphotSourceStats.c	(revision 29936)
@@ -14,6 +14,5 @@
     psAssert (recipe, "missing recipe?");
 
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // skip the chisq image (optionally?)
Index: trunk/psphot/src/psphotStackChisqImage.c
===================================================================
--- trunk/psphot/src/psphotStackChisqImage.c	(revision 29902)
+++ trunk/psphot/src/psphotStackChisqImage.c	(revision 29936)
@@ -8,6 +8,4 @@
 bool psphotStackChisqImage (pmConfig *config, const pmFPAview *view, const char *ruleDet, const char *ruleCnv)
 {
-    bool status = false;
-
     psTimerStart ("psphot.chisq.image");
 
@@ -17,6 +15,5 @@
     pmReadout *chiReadout = NULL;
 
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, "PSPHOT.INPUT");
 
     // loop over the available readouts
@@ -28,7 +25,4 @@
         }
     }
-
-    num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
 
     psMetadataAddS32(config->arguments, PS_LIST_TAIL, "PSPHOT.CHISQ.NUM", PS_META_REPLACE, "", num);
@@ -125,6 +119,5 @@
     psAssert (status, "programming error: must define PSPHOT.CHISQ.NUM");
 
-    int inputNum = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int inputNum = psphotFileruleCount(config, "PSPHOT.INPUT");
 
     pmFPAfileRemoveSingle (config->files, filerule, chisqNum);
Index: trunk/psphot/src/psphotStackImageLoop.c
===================================================================
--- trunk/psphot/src/psphotStackImageLoop.c	(revision 29902)
+++ trunk/psphot/src/psphotStackImageLoop.c	(revision 29936)
@@ -105,11 +105,8 @@
 bool GetAstrometryFPA (pmConfig *config, pmFPAview *view) {
 
-    bool status = false;
-
     bool foundAstrometry = false;
     bool bilevelAstrometry = false;
 
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, "PSPHOT.INPUT");
 
     // loop over the available readouts
@@ -158,8 +155,5 @@
 bool GetAstrometryChip (pmConfig *config, pmFPAview *view, bool bilevelAstrometry) {
 
-    bool status = false;
-
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, "PSPHOT.INPUT");
 
     // loop over the available readouts
@@ -220,10 +214,7 @@
 bool SetAstrometryFPA (pmConfig *config, pmFPAview *view, bool bilevelAstrometry) {
 
-    bool status = false;
-
     if (!bilevelAstrometry) return true;
 
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, "PSPHOT.INPUT");
 
     // loop over the available readouts
Index: trunk/psphot/src/psphotStackMatchPSFs.c
===================================================================
--- trunk/psphot/src/psphotStackMatchPSFs.c	(revision 29902)
+++ trunk/psphot/src/psphotStackMatchPSFs.c	(revision 29936)
@@ -8,6 +8,5 @@
     psAssert(recipe, "We've thrown an error on this before.");
 
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, "PSPHOT.INPUT");
 
     // 'options' carries info needed to perform the stack matching
Index: trunk/psphot/src/psphotStackReadout.c
===================================================================
--- trunk/psphot/src/psphotStackReadout.c	(revision 29902)
+++ trunk/psphot/src/psphotStackReadout.c	(revision 29936)
@@ -52,5 +52,5 @@
     // load the psf model, if suppled.  FWHM_X,FWHM_Y,etc are determined and saved on
     // readout->analysis XXX this function currently only works with a single PSPHOT.INPUT
-    if (!psphotLoadPSF (config, view)) {
+    if (!psphotLoadPSF (config, view, STACK_RAW)) {
         psError (PSPHOT_ERR_UNKNOWN, false, "error loading psf model");
         return psphotReadoutCleanup (config, view, STACK_OUT);
Index: trunk/psphot/src/psphotSubtractBackground.c
===================================================================
--- trunk/psphot/src/psphotSubtractBackground.c	(revision 29902)
+++ trunk/psphot/src/psphotSubtractBackground.c	(revision 29936)
@@ -132,11 +132,10 @@
     psAssert (recipe, "missing recipe?");
 
-    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
-    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
+    int num = psphotFileruleCount(config, filerule);
 
     // loop over the available readouts
     for (int i = 0; i < num; i++) {
 	if (!psphotSubtractBackgroundReadout (config, view, filerule, i, recipe)) {
-            psError (PSPHOT_ERR_CONFIG, false, "failed to subtract background for PSPHOT.INPUT entry %d", i);
+	    psError (PSPHOT_ERR_CONFIG, false, "failed to subtract background for %s entry %d", filerule, i);
 	    return false;
 	}
Index: trunk/psphot/src/psphotTest.c
===================================================================
--- trunk/psphot/src/psphotTest.c	(revision 29902)
+++ trunk/psphot/src/psphotTest.c	(revision 29936)
@@ -137,5 +137,5 @@
     psMetadataItem *mdi;
 
-    psMetadataConfigWrite (header, argv[2]);
+    psMetadataConfigWrite (header, argv[2], NULL);
 
     // attempt to write image with NAXIS = 0
