Index: /branches/eam_branches/ipp-20150112/psphot/src/Makefile.am
===================================================================
--- /branches/eam_branches/ipp-20150112/psphot/src/Makefile.am	(revision 38056)
+++ /branches/eam_branches/ipp-20150112/psphot/src/Makefile.am	(revision 38057)
@@ -1,23 +1,8 @@
 lib_LTLIBRARIES = libpsphot.la
-
-if HAVE_SVNVERSION
-PSPHOT_VERSION=`$(SVNVERSION) ..`
-else
-PSPHOT_VERSION="UNKNOWN"
-endif
-
-if HAVE_SVN
-PSPHOT_BRANCH=`$(SVN) info .. | $(SED) -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'`
-PSPHOT_SOURCE=`$(SVN) info | $(SED) -n -e 's/Repository UUID: // p'`
-else
-PSPHOT_BRANCH="UNKNOWN"
-PSPHOT_SOURCE="UNKNOWN"
-endif
 
 # Force recompilation of psphotVersion.c, since it gets the version information
 psphotVersion.c: psphotVersionDefinitions.h
 psphotVersionDefinitions.h: psphotVersionDefinitions.h.in FORCE
-	-$(RM) psphotVersionDefinitions.h
-	$(SED) -e "s|@PSPHOT_VERSION@|\"$(PSPHOT_VERSION)\"|" -e "s|@PSPHOT_BRANCH@|\"$(PSPHOT_BRANCH)\"|" -e "s|@PSPHOT_SOURCE@|\"$(PSPHOT_SOURCE)\"|" psphotVersionDefinitions.h.in > psphotVersionDefinitions.h
+	pslib-setsvnversion.pl PSPHOT psphotVersionDefinitions.h.in psphotVersionDefinitions.h
 FORCE: ;
 
@@ -230,4 +215,5 @@
 	psphotLoadSRCTEXT.c            \
 	psphotReadoutCleanup.c	       \
+	psphotStatsFile.c	       \
 	psphotSourcePlots.c	       \
 	psphotRadialPlot.c	       \
Index: /branches/eam_branches/ipp-20150112/psphot/src/psphot.h
===================================================================
--- /branches/eam_branches/ipp-20150112/psphot/src/psphot.h	(revision 38056)
+++ /branches/eam_branches/ipp-20150112/psphot/src/psphot.h	(revision 38057)
@@ -45,4 +45,6 @@
 bool            psphotReadoutCleanup (pmConfig *config, const pmFPAview *view, const char *filerule);
 bool            psphotReadoutCleanupReadout (pmConfig *config, const pmFPAview *view, const char *filerule, int index, psMetadata *recipe);
+bool            psphotReadoutCleanupMinimal (pmConfig *config, const pmFPAview *view, const char *filerule);
+bool            psphotReadoutCleanupReadoutMinimal (pmConfig *config, const pmFPAview *view, const char *filerule, int index, psMetadata *recipe);
 
 bool            psphotDefineFiles (pmConfig *config, pmFPAfile *input);
Index: /branches/eam_branches/ipp-20150112/psphot/src/psphotCleanup.c
===================================================================
--- /branches/eam_branches/ipp-20150112/psphot/src/psphotCleanup.c	(revision 38056)
+++ /branches/eam_branches/ipp-20150112/psphot/src/psphotCleanup.c	(revision 38057)
@@ -10,4 +10,10 @@
 	(void)pmConfigRecipesCull(config,NULL);
         pmConfigDump(config, dump_file);
+    }
+
+    // if the program has stats write it out
+    psphotStatsFile *statsFile = psphotStatsFileGet();
+    if (statsFile) {
+        psphotStatsFileSave(config, statsFile);
     }
 
Index: /branches/eam_branches/ipp-20150112/psphot/src/psphotExtendedSourceFits.c
===================================================================
--- /branches/eam_branches/ipp-20150112/psphot/src/psphotExtendedSourceFits.c	(revision 38056)
+++ /branches/eam_branches/ipp-20150112/psphot/src/psphotExtendedSourceFits.c	(revision 38057)
@@ -534,4 +534,12 @@
               Nconvolve ++;
               if (!(modelFit->flags & badModel)) {
+                  float *PAR = modelFit->params->data.F32;
+                  psEllipseAxes axes = pmPSF_ModelToAxes (PAR, modelFit->class->useReff);
+                  if (axes.major >= 100) {
+                      Nfail ++;
+                      source->mode2 |= PM_SOURCE_MODE2_EXT_FITS_FAIL;
+                      psFree(modelFit);
+                      continue;
+                  }
                   NconvolvePass ++;
 		  source->mode |= PM_SOURCE_MODE_EXTENDED_FIT;
Index: /branches/eam_branches/ipp-20150112/psphot/src/psphotFitSourcesLinear.c
===================================================================
--- /branches/eam_branches/ipp-20150112/psphot/src/psphotFitSourcesLinear.c	(revision 38056)
+++ /branches/eam_branches/ipp-20150112/psphot/src/psphotFitSourcesLinear.c	(revision 38057)
@@ -688,5 +688,7 @@
     assert (modelVar);
 
-    psMetadataRemoveKey (readout->analysis, "PSPHOT.MODEL.VAR"); 
+    if (modelVar) {
+      psMetadataRemoveKey (readout->analysis, "PSPHOT.MODEL.VAR"); 
+    }
 
     // clear modelVar pointers for all of the source models
Index: /branches/eam_branches/ipp-20150112/psphot/src/psphotFullForce.c
===================================================================
--- /branches/eam_branches/ipp-20150112/psphot/src/psphotFullForce.c	(revision 38056)
+++ /branches/eam_branches/ipp-20150112/psphot/src/psphotFullForce.c	(revision 38057)
@@ -10,4 +10,9 @@
     pmConfig *config = psphotFullForceArguments (argc, argv);
     assert(config);
+
+    if (!psphotStatsFileOpen(config)) {
+        psErrorStackPrint(stderr, "Error creating statsFile\n");
+        exit (psphotGetExitStatus());
+    }
 
     psphotVersionPrint();
@@ -25,5 +30,5 @@
     }
 
-    psLogMsg ("psphot", 3, "complete psphot run: %f sec\n", psTimerMark ("complete"));
+    psLogMsg ("psphot", 3, "complete psphotFullForce run: %f sec\n", psTimerMark ("complete"));
 
     psErrorCode exit_status = psphotGetExitStatus();
Index: /branches/eam_branches/ipp-20150112/psphot/src/psphotFullForceArguments.c
===================================================================
--- /branches/eam_branches/ipp-20150112/psphot/src/psphotFullForceArguments.c	(revision 38056)
+++ /branches/eam_branches/ipp-20150112/psphot/src/psphotFullForceArguments.c	(revision 38057)
@@ -65,4 +65,11 @@
         psMetadataAddStr (config->arguments, PS_LIST_TAIL, "CHIP_SELECTIONS", PS_DATA_STRING, "", argv[N]);
         psArgumentRemove (N, &argc, argv);
+    }
+
+    if ((N = psArgumentGet(argc, argv, "-stats"))) {
+        psArgumentRemove(N, &argc, argv);
+        psMetadataAddStr(config->arguments, PS_LIST_TAIL, "STATS", PS_DATA_STRING,
+                         "Filename for statistics of output file", argv[N]);
+        psArgumentRemove(N, &argc, argv);
     }
 
Index: /branches/eam_branches/ipp-20150112/psphot/src/psphotGalaxyParams.c
===================================================================
--- /branches/eam_branches/ipp-20150112/psphot/src/psphotGalaxyParams.c	(revision 38056)
+++ /branches/eam_branches/ipp-20150112/psphot/src/psphotGalaxyParams.c	(revision 38057)
@@ -152,5 +152,5 @@
 
 // set this to 0 to run without threading
-# if (0)	    
+# if (1)	    
             if (!psThreadJobAddPending(job)) {
                 psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
@@ -270,4 +270,6 @@
     badModel |= PM_MODEL_PCM_FAIL_GUESS;
 
+    badModel |= PM_MODEL_STATUS_LIMITS;
+
     pmModelType sersicModelType = pmModelClassGetType("PS_MODEL_SERSIC");
 
@@ -334,4 +336,16 @@
         if (!sersicModel) SKIP (Nskip8);
         if (sersicModel->flags & badModel) SKIP (Nskip8);
+
+        // check out the model params and skip those that are too large
+        psF32 *PAR = sersicModel->params->data.F32;
+        psF32 Xo = PAR[PM_PAR_XPOS];
+        psF32 Yo = PAR[PM_PAR_YPOS];
+        psF32 Io = PAR[PM_PAR_I0];
+        //
+        // The half light radius to the major axis of the model
+        psEllipseAxes axes = pmPSF_ModelToAxes (PAR, sersicModel->class->useReff);
+        if (axes.major >= 100) SKIP (Nskip8);
+
+        source->extpars->ghalfLightRadius = axes.major;
 	Next ++;
 
@@ -353,16 +367,7 @@
             // do we need this?
             source->mode |= PM_SOURCE_MODE_NONLINEAR_FIT;
-#ifdef notdef
-#endif
-        }
-
-
-        psF32 *PAR = sersicModel->params->data.F32;
-        psF32 Xo = PAR[PM_PAR_XPOS];
-        psF32 Yo = PAR[PM_PAR_YPOS];
-        psF32 Io = PAR[PM_PAR_I0];
-        // For now: set the half light radius to the major axis of the model
-        psEllipseAxes axes = pmPSF_ModelToAxes (PAR, sersicModel->class->useReff);
-        source->extpars->ghalfLightRadius = axes.major;
+        }
+
+
         // and we will integrate out to 2 * half light radius
         psF32 R = 2 * axes.major;
Index: /branches/eam_branches/ipp-20150112/psphot/src/psphotInternal.h
===================================================================
--- /branches/eam_branches/ipp-20150112/psphot/src/psphotInternal.h	(revision 38056)
+++ /branches/eam_branches/ipp-20150112/psphot/src/psphotInternal.h	(revision 38057)
@@ -14,4 +14,5 @@
 #include <psmodules.h>
 #include "psphot.h"
+#include "psphotStatsFile.h"
 
 #endif
Index: /branches/eam_branches/ipp-20150112/psphot/src/psphotLensing.c
===================================================================
--- /branches/eam_branches/ipp-20150112/psphot/src/psphotLensing.c	(revision 38056)
+++ /branches/eam_branches/ipp-20150112/psphot/src/psphotLensing.c	(revision 38057)
@@ -138,4 +138,6 @@
     psVector *psf_e1sh = psVectorAllocEmpty (300, PS_TYPE_F32);
     psVector *psf_e2sh = psVectorAllocEmpty (300, PS_TYPE_F32);
+    psVector *psf_e1   = psVectorAllocEmpty (300, PS_TYPE_F32);
+    psVector *psf_e2   = psVectorAllocEmpty (300, PS_TYPE_F32);
     psVector *xPos     = psVectorAllocEmpty (300, PS_TYPE_F32);
     psVector *yPos     = psVectorAllocEmpty (300, PS_TYPE_F32);
@@ -178,4 +180,8 @@
 
 	pmModel *model = source->modelPSF;
+
+	float R  = source->moments->Mxx + source->moments->Myy;
+	source->lensingOBJ->e1 = (source->moments->Mxx - source->moments->Myy) / R;
+	source->lensingOBJ->e2 = 2*source->moments->Mxy / R;
 
         psVectorAppend (xPos, model->params->data.F32[PM_PAR_XPOS]);
@@ -191,4 +197,6 @@
         psVectorAppend (psf_e1sh, source->lensingOBJ->shear->e1);
         psVectorAppend (psf_e2sh, source->lensingOBJ->shear->e2);
+        psVectorAppend (psf_e1,   source->lensingOBJ->e1);
+        psVectorAppend (psf_e2,   source->lensingOBJ->e2);
 	Npsf ++;
     }
@@ -247,4 +255,7 @@
     pmTrend2D *trend_e2sh = pmTrend2DAlloc (PM_TREND_MAP, readout->image, Nx, Ny, stats);
 
+    pmTrend2D *trend_e1   = pmTrend2DAlloc (PM_TREND_MAP, readout->image, Nx, Ny, stats);
+    pmTrend2D *trend_e2   = pmTrend2DAlloc (PM_TREND_MAP, readout->image, Nx, Ny, stats);
+
     bool goodFit = false;
     if (!pmTrend2DFit (&goodFit, trendX11sm, NULL, 0xff, xPos, yPos, psfX11sm, NULL)) { psWarning ("failed to measure X11 smear trend"); }
@@ -259,4 +270,7 @@
     if (!pmTrend2DFit (&goodFit, trend_e1sh, NULL, 0xff, xPos, yPos, psf_e1sh, NULL)) { psWarning ("failed to measure  e1 shear trend"); }
     if (!pmTrend2DFit (&goodFit, trend_e2sh, NULL, 0xff, xPos, yPos, psf_e2sh, NULL)) { psWarning ("failed to measure  e2 shear trend"); }
+
+    if (!pmTrend2DFit (&goodFit, trend_e1,   NULL, 0xff, xPos, yPos, psf_e1,   NULL)) { psWarning ("failed to measure  e1 trend"); }
+    if (!pmTrend2DFit (&goodFit, trend_e2,   NULL, 0xff, xPos, yPos, psf_e2,   NULL)) { psWarning ("failed to measure  e2 trend"); }
 
     // evaluate the PSF trend maps at the location of all sources
@@ -295,4 +309,7 @@
 	smear->e1  = pmTrend2DEval (trend_e1sm, xPos, yPos);
 	smear->e2  = pmTrend2DEval (trend_e2sm, xPos, yPos);
+
+	lensing->e1  = pmTrend2DEval (trend_e1, xPos, yPos);
+	lensing->e2  = pmTrend2DEval (trend_e2, xPos, yPos);
     }
 
@@ -309,4 +326,7 @@
     psFree (trend_e2sh);
 
+    psFree (trend_e1);
+    psFree (trend_e2);
+
     psFree (psfX11sm);
     psFree (psfX12sm);
@@ -314,4 +334,5 @@
     psFree (psf_e1sm);
     psFree (psf_e2sm);
+
     psFree (psfX11sh);
     psFree (psfX12sh);
@@ -319,4 +340,8 @@
     psFree (psf_e1sh);
     psFree (psf_e2sh);
+
+    psFree (psf_e1);
+    psFree (psf_e2);
+
     psFree (xPos    );
     psFree (yPos    );
Index: /branches/eam_branches/ipp-20150112/psphot/src/psphotModelBackground.c
===================================================================
--- /branches/eam_branches/ipp-20150112/psphot/src/psphotModelBackground.c	(revision 38056)
+++ /branches/eam_branches/ipp-20150112/psphot/src/psphotModelBackground.c	(revision 38057)
@@ -277,4 +277,5 @@
         psFree(binning);
         psFree(rng);
+        psFree(dQ);
         return false;
     }
@@ -386,5 +387,10 @@
 
     if (!psphotModelBackgroundReadout(model->image, modelStdev->image, model->analysis, readout, binning, config, false)) {
-        psError(PS_ERR_UNKNOWN, false, "Unable to generate background model");
+        int lastError = psErrorCodeLast();
+        if (lastError == PSPHOT_ERR_DATA) {
+            // depending on context this value may or may not be used
+            psMetadataAddS32 (readout->analysis, PS_LIST_TAIL, "PSPHOT_QUALITY", PS_META_REPLACE, "quality error due to poor data", lastError);
+        }
+        psError(lastError, false, "Unable to generate background model");
         return false;
     }
@@ -404,5 +410,6 @@
     for (int i = 0; i < num; i++) {
         if (!psphotModelBackgroundReadoutFileIndex(config, view, filerule, i)) {
-	    psError (PSPHOT_ERR_CONFIG, false, "failed to model background for %s entry %d", filerule, i);
+            int lastError = psErrorCodeLast();
+	    psError (lastError ? lastError : PSPHOT_ERR_CONFIG, false, "failed to model background for %s entry %d", filerule, i);
             return false;
         }
Index: /branches/eam_branches/ipp-20150112/psphot/src/psphotReadoutCleanup.c
===================================================================
--- /branches/eam_branches/ipp-20150112/psphot/src/psphotReadoutCleanup.c	(revision 38056)
+++ /branches/eam_branches/ipp-20150112/psphot/src/psphotReadoutCleanup.c	(revision 38057)
@@ -54,4 +54,10 @@
     psArray      *sources    = detections ? detections->allSources : NULL;
     // XXX where do we free these, in here (psMetadataRemove?)
+
+    int quality = psMetadataLookupS32 (&status, readout->analysis, "PSPHOT_QUALITY");
+    if (quality) {
+        // if there is no stats file this will be a no-op
+        psphotStatsFileSetQuality(quality);
+    }
 
     // use the psf-model to measure FWHM stats
@@ -111,2 +117,87 @@
     return true;
 }
+
+// for now, let's store the detections on the readout->analysis for each readout
+bool psphotReadoutCleanupMinimal (pmConfig *config, const pmFPAview *view, const char *filerule)
+{
+    bool status = true;
+
+    // remove internal pmFPAfiles, if created
+    if (psErrorCodeLast() == (psErrorCode) PSPHOT_ERR_DATA) {
+        psErrorStackPrint(stderr, "Error in the psphot readout analysis");
+        psErrorClear();
+    }
+    if (psErrorCodeLast() != PS_ERR_NONE) {
+        return false;
+    }
+
+    // select the appropriate recipe information
+    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSPHOT_RECIPE);
+    psAssert (recipe, "missing recipe?");
+
+    int num = psphotFileruleCount(config, filerule);
+
+    // loop over the available readouts
+    for (int i = 0; i < num; i++) {
+	if (!psphotReadoutCleanupReadoutMinimal (config, view, filerule, i, recipe)) {
+            psError (PSPHOT_ERR_CONFIG, false, "failed on psphotReadoutCleanupMinimal for %s entry %d", filerule, i);
+	    return false;
+	}
+    }
+
+    // XXX move this to top of loop?
+    pmKapaClose ();
+
+    return true;
+}
+
+// psphotReadoutCleanupMinimal is called on exit from psphotReadoutMinimal.  If the last raised error is
+// not a DATA error, then there was a serious problem.  Only in this case, or if the fail
+// on the stats measurement, do we return false
+bool psphotReadoutCleanupReadoutMinimal (pmConfig *config, const pmFPAview *view, const char *filerule, int index, psMetadata *recipe) {
+
+    bool status = true;
+
+    // find the currently selected readout
+    pmFPAfile *file = pmFPAfileSelectSingle(config->files, filerule, index); // File of interest
+    psAssert (file, "missing file?");
+
+    pmReadout *readout = pmFPAviewThisReadout(view, file->fpa);
+    psAssert (readout, "missing readout?");
+
+    // when psphotReadoutCleanupMinimal is called, these are not necessarily defined
+    pmPSF        *psf        = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.PSF");
+    pmDetections *detections = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.DETECTIONS");
+    psArray      *sources    = detections ? detections->allSources : NULL;
+    // XXX where do we free these, in here (psMetadataRemove?)
+
+    // create an output header with stats results currently saved on readout->analysis
+    psMetadata *header = psphotDefineHeader (readout->analysis);
+
+    // write NSTARS to the image header
+    psphotSetHeaderNstars (header, sources);
+
+    // save the results of the analysis
+    // this should happen way up stream (when needed?)
+    psMetadataAdd (readout->analysis, PS_LIST_TAIL, "PSPHOT.HEADER",  PS_DATA_METADATA | PS_META_REPLACE, "header stats", header);
+
+    if (psf) {
+	// XXX this seems a little silly : we saved the psf on readout->analysis above, but now
+	// we are moving it to chip->analysis.
+        // save the psf for possible output.  if there was already an entry, it was loaded from external sources
+        // the new one may have been updated or modified, so replace the existing entry.  We
+        // are required to save it on the chip, but this will cause problems if we ever want to
+        // run psphot on an unmosaiced image
+        pmCell *cell = readout->parent;
+        pmChip *chip = cell->parent;
+        psMetadataAdd (chip->analysis, PS_LIST_TAIL, "PSPHOT.PSF", PS_DATA_UNKNOWN | PS_META_REPLACE,  "psphot psf", psf);
+    }
+
+    if (psErrorCodeLast() != PS_ERR_NONE) {
+        psErrorStackPrint(stderr, "unexpected remaining errors");
+        abort();
+    }
+
+    psFree (header);
+    return true;
+}
Index: /branches/eam_branches/ipp-20150112/psphot/src/psphotReadoutMinimal.c
===================================================================
--- /branches/eam_branches/ipp-20150112/psphot/src/psphotReadoutMinimal.c	(revision 38056)
+++ /branches/eam_branches/ipp-20150112/psphot/src/psphotReadoutMinimal.c	(revision 38057)
@@ -90,4 +90,6 @@
     psphotKronIterate(config, view, filerule, 1);
 
+    psphotChipParams (config, view, filerule);
+
     // measure source size for the remaining sources
     psphotSourceSize (config, view, filerule, false);
@@ -112,4 +114,4 @@
 
     // create the exported-metadata and free local data
-    return psphotReadoutCleanup (config, view, filerule);
+    return psphotReadoutCleanupMinimal (config, view, filerule);
 }
Index: /branches/eam_branches/ipp-20150112/psphot/src/psphotStandAlone.h
===================================================================
--- /branches/eam_branches/ipp-20150112/psphot/src/psphotStandAlone.h	(revision 38056)
+++ /branches/eam_branches/ipp-20150112/psphot/src/psphotStandAlone.h	(revision 38057)
@@ -10,4 +10,5 @@
 #include <psmodules.h>
 #include "psphot.h"
+#include "psphotStatsFile.h"
 
 // Top level functions
Index: /branches/eam_branches/ipp-20150112/psphot/src/psphotStatsFile.c
===================================================================
--- /branches/eam_branches/ipp-20150112/psphot/src/psphotStatsFile.c	(revision 38057)
+++ /branches/eam_branches/ipp-20150112/psphot/src/psphotStatsFile.c	(revision 38057)
@@ -0,0 +1,111 @@
+/** @file psphotStatsFile.c
+ *
+ *  @brief functions for managing the stats file (or return NULL, or exit with an error)
+ *  @ingroup psphot
+ *
+ *  @author IfA
+ *  @version $Revision: 1.38 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2009-02-13 21:54:32 $
+ *  Copyright 2009 Institute for Astronomy, University of Hawaii
+ */
+
+#include "psphotStandAlone.h"
+
+static psphotStatsFile *theStatsFile = NULL;
+
+static void psphotStatsFileFree (psphotStatsFile *statsFile) {
+    psFree (statsFile->name);
+    psFree (statsFile->md);
+    theStatsFile = NULL;
+}
+
+psphotStatsFile *psphotStatsFileAlloc () {
+
+    psphotStatsFile *statsFile = psAlloc(sizeof(psphotStatsFile));
+    psMemSetDeallocator(statsFile, (psFreeFunc)psphotStatsFileFree);
+
+    statsFile->f = NULL;
+    statsFile->name = NULL;
+    statsFile->md = NULL;
+
+    return statsFile;
+}
+
+// Open the statistics file
+psphotStatsFile *psphotStatsFileOpen (pmConfig *config) {
+
+    bool mdok;
+
+    char *name = psMetadataLookupStr(&mdok, config->arguments, "STATS"); ///< Filename for statistics
+
+    if (!name) return NULL;
+
+    psphotStatsFile *statsFile = psphotStatsFileAlloc ();
+    statsFile->name = psMemIncrRefCounter(name);
+
+    if (!mdok) return statsFile; // XXX this is probably a config error, but treat as ok for now
+    if (!name) return statsFile;
+    if (strlen(name) == 0) return statsFile;
+
+    psString resolved = pmConfigConvertFilename(name, config, true, true);
+
+    statsFile->f = fopen(resolved, "w");
+    if (!statsFile->f) {
+	psError(PS_ERR_IO, true, "Unable to open statistics file %s for writing.\n", resolved);
+	psFree(resolved);
+    }
+    psFree(resolved);
+
+    statsFile->md = psMetadataAlloc();
+    psMetadataAddS32(statsFile->md, PS_LIST_TAIL, "QUALITY", 0, "No problems", 0);
+
+    // the global copy
+    theStatsFile = statsFile;
+
+    return statsFile;
+}
+
+bool psphotStatsFileSave (pmConfig *config, psphotStatsFile *statsFile) {
+
+    if (!statsFile) return true;
+    if (!statsFile->md) return true;
+
+    // Write out summary statistics
+//    psMetadataAddF32(statsFile->md, PS_LIST_TAIL, "DT_WARP", 0, "Time for warp completion", psTimerMark("psphot"));
+
+    // convert the psMetadata to a string block
+    const char *statsMDC = psMetadataConfigFormat(statsFile->md);
+    if (!statsMDC) {
+	psError(psErrorCodeLast(), false, "Unable to get statistics file.");
+	return false;
+    }
+
+    if (fprintf(statsFile->f, "%s", statsMDC) != strlen(statsMDC)) {
+	psError(PSPHOT_ERR_IO, true, "Unable to write statistics file.");
+	return false;
+    }
+    psFree(statsMDC);
+
+    if (fclose(statsFile->f) == EOF) {
+	psError(PSPHOT_ERR_IO, true, "Unable to close statistics file.");
+	statsFile->f = NULL;
+	return false;
+    }
+    statsFile->f = NULL;
+
+    pmConfigRunFilenameAddWrite(config, "STATS", statsFile->name);
+    psFree(statsFile);
+
+    return true;
+}
+
+psphotStatsFile *psphotStatsFileGet () {
+    return theStatsFile;
+}
+
+void psphotStatsFileSetQuality (int quality) {
+    psphotStatsFile *statsFile = psphotStatsFileGet();
+    if (!statsFile) return;
+
+    psMetadataAddS32(statsFile->md, PS_LIST_TAIL, "QUALITY", PS_META_REPLACE, "quality value", quality);
+}
Index: /branches/eam_branches/ipp-20150112/psphot/src/psphotStatsFile.h
===================================================================
--- /branches/eam_branches/ipp-20150112/psphot/src/psphotStatsFile.h	(revision 38057)
+++ /branches/eam_branches/ipp-20150112/psphot/src/psphotStatsFile.h	(revision 38057)
@@ -0,0 +1,16 @@
+#ifndef PSPHOT_STATS_FILE_H
+#define PSPHOT_STATS_FILE_H
+
+
+typedef struct {
+    FILE *f;                            // File stream for statistics
+    char *name;                         // Filename for statistics
+    psMetadata *md;                     // Container for statistics
+} psphotStatsFile;
+    
+psphotStatsFile *psphotStatsFileOpen (pmConfig  *config);
+psphotStatsFile *psphotStatsFileGet ();
+bool            psphotStatsFileSave (pmConfig *config, psphotStatsFile *statsFile);
+void            psphotStatsFileSetQuality (int quality);
+
+#endif
Index: /branches/eam_branches/ipp-20150112/psphot/src/psphotSubtractBackground.c
===================================================================
--- /branches/eam_branches/ipp-20150112/psphot/src/psphotSubtractBackground.c	(revision 38056)
+++ /branches/eam_branches/ipp-20150112/psphot/src/psphotSubtractBackground.c	(revision 38057)
@@ -29,5 +29,5 @@
     // user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
     psImageMaskType maskVal = psMetadataLookupImageMask(&status, recipe, "MASK.PSPHOT"); // Mask value for bad pixels
-    assert (maskVal);
+    psAssert (maskVal, "MASK.PSPHOT missing from recipe");
 
     psImageBinning *binning = psMetadataLookupPtr(&status, model->analysis, "PSPHOT.BACKGROUND.BINNING");
