Index: branches/sc_branches/trunkTest/psModules/src/camera/pmFPAfileIO.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/camera/pmFPAfileIO.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/camera/pmFPAfileIO.c	(revision 29060)
@@ -24,17 +24,26 @@
 #include "pmFPAfileFitsIO.h"
 #include "pmFPAfileFringeIO.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmResiduals.h"
-#include "pmGrowthCurve.h"
-#include "pmTrend2D.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
-#include "pmSource.h"
+#include "pmPSFtry.h"
+
 #include "pmSourceIO.h"
-#include "pmResiduals.h"
 #include "pmPSF_IO.h"
+
 #include "pmAstrometryModel.h"
 #include "pmAstrometryRefstars.h"
Index: branches/sc_branches/trunkTest/psModules/src/camera/pmReadoutFake.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/camera/pmReadoutFake.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/camera/pmReadoutFake.c	(revision 29060)
@@ -10,19 +10,26 @@
 #include "pmFPA.h"
 
-#include "pmMoments.h"
+
+#include "pmTrend2D.h"
 #include "pmResiduals.h"
 #include "pmGrowthCurve.h"
-#include "pmTrend2D.h"
-#include "pmPSF.h"
-#include "pmModel.h"
-#include "pmModelClass.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
 #include "pmSource.h"
-#include "pmSourceUtils.h"
-#include "pmModelUtils.h"
+#include "pmSourceFitModel.h"
+#include "pmPSF.h"
+#include "pmPSFtry.h"
+
 #include "pmSourceGroups.h"
-
 #include "pmReadoutFake.h"
 
Index: branches/sc_branches/trunkTest/psModules/src/camera/pmReadoutFake.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/camera/pmReadoutFake.h	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/camera/pmReadoutFake.h	(revision 29060)
@@ -2,14 +2,14 @@
 #define PM_READOUT_FAKE_H
 
-#include <pslib.h>
-#include <pmHDU.h>
-#include <pmFPA.h>
-
-#include <pmMoments.h>
-#include <pmResiduals.h>
-#include <pmGrowthCurve.h>
-#include <pmTrend2D.h>
-#include <pmPSF.h>
-#include <pmSourceMasks.h>
+// #include <pslib.h>
+// #include <pmHDU.h>
+// #include <pmFPA.h>
+// 
+// #include <pmMoments.h>
+// #include <pmResiduals.h>
+// #include <pmGrowthCurve.h>
+// #include <pmTrend2D.h>
+// #include <pmPSF.h>
+// #include <pmSourceMasks.h>
 
 /// Set threading
Index: branches/sc_branches/trunkTest/psModules/src/config/pmConfig.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/config/pmConfig.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/config/pmConfig.c	(revision 29060)
@@ -32,4 +32,5 @@
 
 #include "pmConfig.h"
+#include "pmVisualUtils.h"
 
 #ifdef HAVE_NEBCLIENT
@@ -638,4 +639,25 @@
         psArgumentVerbosity(argc, argv);
         // XXX: substitute the string for the default log level for "2".
+    }
+
+    // Set the visualization levels
+    // argument format is: -visual (facil) (level)
+    while ((argNum = psArgumentGet(*argc, argv, "-visual"))) {
+        if ( (*argc < argNum + 3) ) {
+            psError(PS_ERR_IO, true, "-visual switch specified without facility and level.");
+            return NULL;
+        }
+        psArgumentRemove(argNum, argc, argv);
+        pmVisualSetLevel(argv[argNum], atoi(argv[argNum+1]));
+        psArgumentRemove(argNum, argc, argv);
+        psArgumentRemove(argNum, argc, argv);
+    }
+    if ((argNum = psArgumentGet(*argc, argv, "-visual-all"))) {
+        pmVisualSetLevel(".", 10);
+        psArgumentRemove(argNum, argc, argv);
+    }
+    if ((argNum = psArgumentGet(*argc, argv, "-visual-levels"))) {
+        pmVisualPrintLevels(stdout);
+        psArgumentRemove(argNum, argc, argv);
     }
 
Index: branches/sc_branches/trunkTest/psModules/src/detrend/pmMaskStats.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/detrend/pmMaskStats.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/detrend/pmMaskStats.c	(revision 29060)
@@ -7,5 +7,4 @@
 
 #include <pslib.h>
-#include <psmodules.h>
 
 #include "pmHDU.h"
@@ -16,4 +15,5 @@
 #include "pmFPAAstrometry.h"
 
+#include "pmMaskStats.h"
 
 #define ESCAPE { \
Index: branches/sc_branches/trunkTest/psModules/src/detrend/pmShutterCorrection.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/detrend/pmShutterCorrection.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/detrend/pmShutterCorrection.c	(revision 29060)
@@ -302,5 +302,5 @@
     PS_ASSERT_PTR_NON_NULL(guess, NULL);
 
-    psMinimization *minInfo = psMinimizationAlloc(15, 0.1); // Minimization information
+    psMinimization *minInfo = psMinimizationAlloc(15, 0.1, 1.0); // Minimization information
 
     psVector *params = psVectorAlloc (3, PS_TYPE_F32); // Fitting parameters
Index: branches/sc_branches/trunkTest/psModules/src/extras/Makefile.am
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/extras/Makefile.am	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/extras/Makefile.am	(revision 29060)
@@ -9,4 +9,5 @@
 	pmKapaPlots.c \
 	pmVisual.c \
+	pmVisualUtils.c \
 	ippStages.c
 
@@ -17,4 +18,5 @@
 	pmKapaPlots.h \
 	pmVisual.h \
+	pmVisualUtils.h \
 	ippDiffMode.h \
 	ippStages.h
Index: branches/sc_branches/trunkTest/psModules/src/extras/pmVisual.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/extras/pmVisual.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/extras/pmVisual.c	(revision 29060)
@@ -21,16 +21,24 @@
 #include "pmAstrometryObjects.h"
 #include "pmSubtractionStamps.h"
+
 #include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmSpan.h"
+#include "pmFootprintSpans.h"
+#include "pmFootprint.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
 #include "pmPSFtry.h"
-#include "pmSource.h"
+
 #include "pmFPAExtent.h"
-
-# if (HAVE_KAPA)
-# include <kapa.h>
-# include "pmVisual.h"
-# include "pmKapaPlots.h"
-# define KAPAX 700
-# define KAPAY 700
 
 #include "pmAstrometryVisual.h"
@@ -38,4 +46,12 @@
 #include "pmStackVisual.h"
 #include "pmSourceVisual.h"
+
+# if (HAVE_KAPA)
+# include <kapa.h>
+#include "pmVisual.h"
+#include "pmKapaPlots.h"
+
+# define KAPAX 700
+# define KAPAY 700
 
 //#define TESTING
@@ -102,5 +118,5 @@
 
     /* Wait up to 1.0 second for a response, then continue */
-    timeout.tv_sec = 2;
+    timeout.tv_sec = 10;
     timeout.tv_usec = 0;
 
Index: branches/sc_branches/trunkTest/psModules/src/extras/pmVisualUtils.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/extras/pmVisualUtils.c	(revision 29060)
+++ branches/sc_branches/trunkTest/psModules/src/extras/pmVisualUtils.c	(revision 29060)
@@ -0,0 +1,505 @@
+/** These utility functions manage the visual level information (equivalent to pmVisual levels)
+ *  @author Eugene Magnier, IfA
+ *  @date June 18, 2010
+ */
+
+/* Include Files  */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <strings.h>
+#include <string.h>
+#include <math.h>
+#include <assert.h>
+#include <pslib.h>
+
+#include <pmVisualUtils.h>
+
+#define PM_UNKNOWN_VISUAL_LEVEL -9999   // we don't know this name's level
+#define PM_DEFAULT_VISUAL_LEVEL -1
+#define PM_THE_OTHER_DEFAULT_VISUAL_LEVEL 0 // ???
+#define MAX_COMPONENT_LENGTH 1024
+
+/** Basic structure for the component tree.  A component is a string of the
+    form aaa.bbb.ccc, and may itself contain further subcomponents.  The
+    Component structure doesn't in fact contain it's full name, but only the
+    last part. */
+
+static pmVisualComponent* cRoot = NULL; // The root of the visual component tree
+
+/** static function prototypes **/
+static void componentFree(pmVisualComponent* comp);
+static pmVisualComponent* componentAlloc(const char *name, int level);
+static int getLevel(const char *facil);
+static void initVisualLevels(void);
+static void componentFree(pmVisualComponent* comp);
+static pmVisualComponent* componentAlloc(const char *name, int level);
+static void doGetVisualLevels(psMetadata *out, const pmVisualComponent* comp, psString parent, int defLevel);
+static void doPrintVisualLevels(const pmVisualComponent* comp, FILE *output, psS32 depth, psS32 defLevel);
+static psS32 doGetVisualLevel(const char *aname);
+static bool componentAdd(const char *addNodeName, psS32 level);
+
+/*****************************************************************************
+Set all visual levels at or below the specified node to zero.
+ *****************************************************************************/
+void pmVisualReset(pmVisualComponent* currentNode)
+{
+    psAssert(currentNode, "impossible");
+
+    psS32 i = 0;
+
+    if (NULL == currentNode) {
+        return;
+    }
+
+    currentNode->level = 0;
+    for (i = 0; i < currentNode->n; i++) {
+        if (!currentNode->subcomp[i]) {
+            psLogMsg("pmVisualReset", PS_LOG_WARN, _("Sub-component %d of node %s in visual tree is NULL."), i, currentNode->name);
+        } else {
+            pmVisualReset(currentNode->subcomp[i]);
+        }
+    }
+    return;
+}
+
+/*****************************************************************************
+Free all visual levels
+ *****************************************************************************/
+void pmVisualCleanup()
+{
+    psFree(cRoot);
+    cRoot = NULL;
+}
+
+// psSetVisualLevel(): add the component named "comp" to the component tree,
+int pmVisualSetLevel(const char *comp,   // component of interest
+                    int level)  // desired visual level
+{
+    PS_ASSERT_STRING_NON_EMPTY(comp, 0);
+
+    char *compName = NULL;
+    int prevLevel = -1;
+
+    // If the root component tree does not exist, then initialize it.
+    if (cRoot == NULL) {
+        initVisualLevels();
+    }
+
+    // If the component name has no leading dot, then supply it.
+    if (comp[0] != '.') {
+        compName = (char *) psAlloc(10 + strlen(comp));
+        strcpy(compName, ".");
+        compName = strcat(compName, comp);
+    } else {
+        compName = (char *) comp;
+    }
+    prevLevel = getLevel(compName);
+    // Add the new component to the component tree.
+    if ( !componentAdd(compName, level) ) {
+        psError(PS_ERR_UNKNOWN, false,
+                _("Failed to set visual level (%d) to '%s'."),
+                level,
+                compName);
+
+        if (comp[0] != '.') {
+            psFree(compName);
+        }
+        //        return false;
+        return -1;
+    }
+
+    if (comp[0] != '.') {
+        psFree(compName);
+    }
+
+    //    return true;
+    return prevLevel;
+}
+
+// Append the function name to the facility
+// NB: declares TARGET!
+#define FACILITY(TARGET, FUNC, FACIL) \
+    size_t _facilLength = strlen(FACIL); /* Length of facility name */ \
+    size_t _funcLength = strlen(FUNC);   /* Length of function name */ \
+    char TARGET[_facilLength + _funcLength + 2]; /* facility + the function name */ \
+    strcpy(&TARGET[0], FACIL); \
+    TARGET[_facilLength] = '.'; \
+    strcpy(&TARGET[_facilLength + 1], FUNC);
+
+int p_pmVisualGetLevel(const char *func, const char *name)
+{
+    PS_ASSERT_STRING_NON_EMPTY(name, 0);
+    PS_ASSERT_STRING_NON_EMPTY(func, 0);
+
+    FACILITY(facility, func, name);
+    return getLevel(facility);
+}
+
+/** the main tool in the pmVisual system : check that the specified level matches the desired level **/
+bool p_pmVisualTestLevel(const char *func, const char *name, int level)
+{
+    PS_ASSERT_STRING_NON_EMPTY(name, false);
+    PS_ASSERT_STRING_NON_EMPTY(func, false);
+
+   // return true if level is set to be shown
+    FACILITY(facility, func, name);
+    bool valid = level <= getLevel(facility);
+
+    if (valid) {
+	psLogMsg ("psModules.visual", PS_LOG_DETAIL, "visualization %s (%d)\n", facility, level);
+    }
+
+    return (valid);
+}
+
+// psPrintVisualLevels(): Simply print all the visual levels in the visual level
+void pmVisualPrintLevels(FILE *output)
+{
+    if (cRoot == NULL) {
+        return;
+    }
+
+    doPrintVisualLevels(cRoot, output, 0, PM_THE_OTHER_DEFAULT_VISUAL_LEVEL);
+}
+
+// generate a metadata of the visual levels (is this really needed?)
+psMetadata *pmVisualLevels(void)
+{
+    if (cRoot == NULL) {
+        return psMetadataAlloc();
+    }
+
+    psMetadata *out = psMetadataAlloc();// Output metadata with the levels
+    doGetVisualLevels(out, cRoot, NULL, PM_THE_OTHER_DEFAULT_VISUAL_LEVEL);
+
+    return out;
+}
+
+/****************************** static functions ******************************/
+
+/*****************************************************************************
+componentAdd(): Adds the component named "addNodeName" to the root tree.
+ *****************************************************************************/
+static bool componentAdd(const char *addNodeName, psS32 level)
+{
+    psAssert(addNodeName, "impossible");
+
+    psS32 i = 0;                        // Loop index variable.
+    char name[MAX_COMPONENT_LENGTH]; // buffer for writeable copy.
+    char *firstComponent = NULL;        // first component of name
+    pmVisualComponent* currentNode = cRoot;
+    psS32 nodeExists = 0;
+
+    // XXX: Verify that this is the correct behavior.
+    if (strcmp("", addNodeName) == 0) {
+        psError(PS_ERR_BAD_PARAMETER_NULL,true,
+                _("Failed to add null component to visual tree."));
+        return false;
+    }
+
+    // Is this the root node? If so, simply set level and return.
+    if (strcmp(".", addNodeName) == 0) {
+        cRoot->level = level;
+        return true;
+    }
+
+    if (addNodeName[0] != '.') {
+        psError(PS_ERR_BAD_PARAMETER_VALUE,true,
+                _("Failed to add '%s' to the root component tree; component must start with '.'."),
+                addNodeName);
+        return false;
+    }
+
+    strncpy(name, addNodeName, MAX_COMPONENT_LENGTH);
+    char *pname = name+1;               // Take off the period
+    // Iterate through the components of addNodeName.  Strip off the first
+    // component of the name, find that in the root tree, or add it if it
+    // does not exist, then move to the next component in the name.
+
+    while (pname != NULL) {
+        firstComponent = pname;
+        pname = strchr(firstComponent, '.');
+        if (pname != NULL) {
+            *pname = '\0';
+            pname++;
+        }
+        nodeExists = 0;
+        for (i = 0; i < currentNode->n; i++) {
+            if (strcmp(currentNode->subcomp[i]->name, firstComponent) == 0) {
+                currentNode = currentNode->subcomp[i];
+                nodeExists = 1;
+                if (pname == NULL) {
+                    currentNode->level = level;
+                }
+            }
+        }
+
+        if (nodeExists == 0) {
+            currentNode->subcomp = psRealloc(currentNode->subcomp,
+                                             (currentNode->n + 1) * sizeof(pmVisualComponent* ));
+            psMemSetPersistent(currentNode->subcomp,true);
+
+            currentNode->n = (currentNode->n) + 1;
+
+            if (pname == NULL) {
+                // This is the final component to add.
+                currentNode->subcomp[(currentNode->n) - 1] =
+                    componentAlloc(firstComponent, level);
+            } else {
+                // We are adding an intermediate component.  The visual level is not defined.
+                // An undefined visual level inherits the visual level of it's parent.  However,
+                // we do not set that specifically here since that would result in a static,
+                // one-time, type of behavior.
+
+                currentNode->subcomp[(currentNode->n) - 1] =
+                    componentAlloc(firstComponent, PM_DEFAULT_VISUAL_LEVEL);
+            }
+            currentNode = currentNode->subcomp[(currentNode->n) - 1];
+        }
+    }
+
+    return true;
+}
+
+/*****************************************************************************
+    doGetVisualLevel()
+ This function recursively searches the root component tree for the
+ component named "name", which is supplied by a parameter.  If it
+ finds that component, it returns the level of that component.
+ Otherwise, it returns ???.
+
+    NOTE: We modified this so that the user may omit the leading "," in a
+    component name.  Since the code was already implemented assuming the "."
+    was required, rather than change all that code, in this function, I
+    simply add a leading "." to the component name if there is none.
+
+    Inputs:
+ name:
+    Outputs:
+ none
+    Returns:
+ The visual level of the "name" component.
+ *****************************************************************************/
+static psS32 doGetVisualLevel(const char *aname)
+{
+    psAssert(aname, "impossible");
+    char name[strlen(aname) + 1];       // need a writeable copy: for strsep()
+    char *pname = name;
+    char *firstComponent = NULL;        // first component of name
+    pmVisualComponent* currentNode = cRoot;
+    psS32 i = 0;
+    psS32 defaultLevel = 0;
+
+    if (NULL == currentNode) {
+        return (PM_UNKNOWN_VISUAL_LEVEL);
+    }
+
+    if (strcmp(".", aname) == 0) {
+        return (cRoot->level);
+    }
+
+    if (aname[0] != '.') {
+        return (PM_UNKNOWN_VISUAL_LEVEL);
+    }
+
+    defaultLevel = cRoot->level;
+    strcpy(name, aname);
+    pname = name+1;
+    while (pname != NULL) {
+        firstComponent = pname;
+        pname = strchr(firstComponent, '.');
+        if (pname != NULL) {
+            *pname = '\0';
+            pname++;
+        }
+        for (i = 0; i < currentNode->n; i++) {
+            if (NULL == currentNode->subcomp[i]) {
+                psLogMsg("p_pmVisualReset", PS_LOG_WARN,
+                         _("Sub-component %d of node %s in visual tree is NULL."),
+                         i, currentNode->name);
+            }
+
+            if (strcmp(currentNode->subcomp[i]->name, firstComponent) == 0) {
+                currentNode = currentNode->subcomp[i];
+                // For level inheritance purpose, we save the level of this
+                // component if it is not DEFAULT.
+                if (currentNode->level != PM_DEFAULT_VISUAL_LEVEL) {
+                    defaultLevel = currentNode->level;
+                }
+                // Determine if this is the last component:
+                if (pname == NULL) {
+                    if (currentNode->level != PM_DEFAULT_VISUAL_LEVEL) {
+                        return (currentNode->level);
+                    } else {
+                        return(defaultLevel);
+                    }
+                }
+            }
+        }
+    }
+    return(defaultLevel);
+}
+
+/*****************************************************************************
+    getLevel()
+ Return a visual level of "name" in the root component tree.  If the
+ exact string of components in "name" does not exist in the root
+ tree, we return the deepest level of the match.
+ *****************************************************************************/
+static int getLevel(const char *name)
+{
+    if (cRoot == NULL) {
+        return (PM_UNKNOWN_VISUAL_LEVEL);
+    }
+
+    psS32 visualLevel;
+
+    // If the component name has no leading dot, then supply it.
+    if (name[0] != '.') {
+        char compName[strlen(name) + 2];
+        compName[0] = '.';
+        strcpy(&compName[1], name);
+
+        visualLevel = doGetVisualLevel(compName);
+    } else {
+        // Search the component root tree, determine the visual level.
+        visualLevel = doGetVisualLevel(name);
+    }
+
+    // XXX: The default visual level is currently set at -1, which is not a
+    // valid visual level.  This is convenient in determining whether or not
+    // a component should inherit the visual level from parent nodes.  However,
+    // it's not clear that -1 should ever be returned by this function.
+    // The SDR is unclear on this point and we should probably request IfA
+    // comment.
+    if (visualLevel == PM_DEFAULT_VISUAL_LEVEL) {
+        visualLevel = PM_THE_OTHER_DEFAULT_VISUAL_LEVEL;
+    }
+
+    return(visualLevel);
+}
+
+/*****************************************************************************
+    doPrintVisualLevels()
+ This function recursively searches the component tree supplied by the
+ parameter "comp" and prints the name and level of each component.
+    Inputs:
+ comp: a node in the component tree.
+ level: the level of that node
+    Outputs:
+ none
+    Returns:
+ null
+ *****************************************************************************/
+static void doPrintVisualLevels(const pmVisualComponent* comp,
+				FILE *output,
+				psS32 depth,
+				psS32 defLevel)
+{
+    psAssert(comp, "impossible");
+
+    char line[1024];
+    psS32 i = 0;
+
+    if (comp->name[0] == '\0') {
+        return;
+    } else {
+        if (comp->level == PM_DEFAULT_VISUAL_LEVEL) {
+	    sprintf(line,"%*s%-*s %d\n", depth, "", 20 - depth, comp->name, defLevel);
+	    fwrite (line, 1, strlen(line), output);
+        } else {
+	    sprintf(line, "%*s%-*s %d\n", depth, "", 20 - depth, comp->name, comp->level);
+	    fwrite (line, 1, strlen(line), output);
+        }
+    }
+
+    for (i = 0; i < comp->n; i++) {
+        if (comp->level == PM_DEFAULT_VISUAL_LEVEL) {
+            doPrintVisualLevels(comp->subcomp[i], output, depth + 1, defLevel);
+        } else {
+            doPrintVisualLevels(comp->subcomp[i], output, depth + 1, comp->level);
+        }
+    }
+}
+
+static void doGetVisualLevels(psMetadata *out, // Output metadata with the visual levels
+                             const pmVisualComponent* comp, // Component to add
+                             psString parent, // Name of parent level
+                             int defLevel // Default level
+                            )
+{
+    if (comp->name[0] == '\0') {
+        return;
+    }
+
+    psString name = psStringCopy(parent); // Name of this level
+    if (comp->name[0] == '.') {
+        psStringAppend(&name, "%s", comp->name + 1);
+    } else if (!parent) {
+        psStringAppend(&name, "%s", comp->name);
+    } else {
+        psStringAppend(&name, ".%s", comp->name);
+    }
+
+    int level = (comp->level == PM_DEFAULT_VISUAL_LEVEL) ? defLevel : comp->level; // Level for component
+    if (name) {
+        psMetadataAddS32(out, PS_LIST_TAIL, name, 0, NULL, level);
+    }
+    for (int i = 0; i < comp->n; i++) {
+        doGetVisualLevels(out, comp->subcomp[i], name, level);
+    }
+
+    psFree(name);
+
+    return;
+}
+
+/*****************************************************************************
+componentAlloc(): allocate memory for a new node, and initialize members.
+ *****************************************************************************/
+static pmVisualComponent* componentAlloc(const char *name, int level)
+{
+    psAssert(name, "impossible");
+
+    pmVisualComponent* comp = psAlloc(sizeof(pmVisualComponent));
+    psMemSetDeallocator(comp, (psFreeFunc) componentFree);
+
+    comp->name = psStringCopy(name);
+    comp->level = level;
+    comp->n = 0;
+    comp->specified = false;
+    comp->subcomp = NULL;
+    return comp;
+}
+
+/*****************************************************************************
+componentFree(): free the current node in the root tree, and all children
+nodes as well.
+ *****************************************************************************/
+static void componentFree(pmVisualComponent* comp)
+{
+    if (comp == NULL) {
+        return;
+    }
+
+    if (comp->subcomp != NULL) {
+        for (psS32 i = 0; i < comp->n; i++) {
+            psFree(comp->subcomp[i]);
+        }
+        psFree(comp->subcomp);
+    }
+    psFree(comp->name);
+}
+
+/*****************************************************************************
+initVisualLevels(): simply initialize the component root tree.
+*****************************************************************************/
+static void initVisualLevels(void)
+{
+    if (cRoot == NULL) {
+        cRoot = componentAlloc(".", PM_DEFAULT_VISUAL_LEVEL);
+    }
+}
Index: branches/sc_branches/trunkTest/psModules/src/extras/pmVisualUtils.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/extras/pmVisualUtils.h	(revision 29060)
+++ branches/sc_branches/trunkTest/psModules/src/extras/pmVisualUtils.h	(revision 29060)
@@ -0,0 +1,33 @@
+/* @file pmVisualUtils.h
+ * @brief functions to create visual diagnostics with the help of 'kapa'
+ * @author Chris Beaumont, IfA
+ *
+ * Copyright 2009 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_VISUAL_UTILS_H
+#define PM_VISUAL_UTILS_H
+
+typedef struct pmVisualComponent
+{
+    const char *name;			///< last part of name of component
+    psS32 level;			///< visual level for this component
+    bool specified;			///< whether the component is specified
+    psS32 n;				///< number of subcomponents
+    struct pmVisualComponent* *subcomp;	///< next level of subcomponents
+}
+pmVisualComponent;
+
+psMetadata *pmVisualLevels(void);
+void pmVisualPrintLevels(FILE *output);
+int pmVisualSetLevel(const char *comp, int level);
+void pmVisualCleanup();
+void pmVisualReset(pmVisualComponent* currentNode);
+
+int p_pmVisualGetLevel(const char *func, const char *name);
+# define pmVisualGetLevel(facil) p_pmVisualGetLevel(__func__, facil)
+
+bool p_pmVisualTestLevel(const char *func, const char *name, int level);
+# define pmVisualTestLevel(facil, level) p_pmVisualTestLevel(__func__, facil, level)
+
+#endif // PM_VISUAL_UTILS_H
Index: branches/sc_branches/trunkTest/psModules/src/imcombine/pmPSFEnvelope.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/imcombine/pmPSFEnvelope.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/imcombine/pmPSFEnvelope.c	(revision 29060)
@@ -10,23 +10,26 @@
 #include "pmHDU.h"
 #include "pmFPA.h"
-#include "pmReadoutFake.h"
-
-#include "pmMoments.h"
+
+#include "pmTrend2D.h"
 #include "pmResiduals.h"
 #include "pmGrowthCurve.h"
-#include "pmTrend2D.h"
-#include "pmPSF.h"
-#include "pmModel.h"
-#include "pmModelClass.h"
-#include "pmModelUtils.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
 #include "pmSource.h"
-#include "pmSourceUtils.h"
 #include "pmSourceFitModel.h"
+#include "pmPSF.h"
 #include "pmPSFtry.h"
 
-
+#include "pmReadoutFake.h"
 #include "pmPSFEnvelope.h"
 #include "pmStackVisual.h"
@@ -410,5 +413,13 @@
     options->chiFluxTrend = false;      // All sources have similar flux, so fitting a trend often fails
 
-    pmSourceFitModelInit(SOURCE_FIT_ITERATIONS, 0.01, VARIANCE_VAL, true);
+    // options which modify the behavior of the model fitting
+    options->fitOptions                = pmSourceFitOptionsAlloc();
+    options->fitOptions->nIter         = SOURCE_FIT_ITERATIONS;
+    options->fitOptions->minTol        = 0.01;
+    options->fitOptions->maxTol        = 1.00;
+    options->fitOptions->poissonErrors = true;
+    options->fitOptions->weight        = VARIANCE_VAL;
+    options->fitOptions->mode          = PM_SOURCE_FIT_PSF;
+
     pmModelClassSetLimits(PM_MODEL_LIMITS_STRICT); // Important for getting a good stack target PSF
 
Index: branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtraction.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtraction.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtraction.c	(revision 29060)
@@ -33,4 +33,9 @@
 #define USE_KERNEL_ERR                  // Use kernel error image?
 #define NUM_COVAR_POS 5                 // Number of positions for covariance calculation
+
+// XXX we need to pass these fwhm values elsewhere.  These should go on one of the structure, but 
+// things are too confusing to do that now.  just save them here.
+static float FWHM1 = NAN;
+static float FWHM2 = NAN;
 
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -752,5 +757,5 @@
 
 
-bool pmSubtractionConvolveStamp(pmSubtractionStamp *stamp, const pmSubtractionKernels *kernels, int footprint)
+bool pmSubtractionConvolveStamp (pmSubtractionStamp *stamp, pmSubtractionKernels *kernels, int footprint)
 {
     PS_ASSERT_PTR_NON_NULL(stamp, false);
@@ -774,4 +779,7 @@
         stamp->convolutions1 = convolveStamp(stamp->convolutions1, stamp->image1, kernels, footprint);
         stamp->convolutions2 = convolveStamp(stamp->convolutions2, stamp->image2, kernels, footprint);
+	if (!pmSubtractionKernelPenaltiesStamp(stamp, kernels)) {
+	    psAbort("failure in penalties");
+	}
         break;
       default:
@@ -1215,4 +1223,5 @@
     bool threaded = pmSubtractionThreaded(); // Running threaded?
 
+    // XXX This is no longer used 
     psImage *convMask = NULL;           // Convolved mask image (common to inputs 1 and 2)
     if (subMask) {
@@ -1413,2 +1422,16 @@
     return true;
 }
+
+bool pmSubtractionGetFWHMs(float *fwhm1, float *fwhm2) {
+
+  *fwhm1 = FWHM1;
+  *fwhm2 = FWHM2;
+  return true;
+}
+
+bool pmSubtractionSetFWHMs(float fwhm1, float fwhm2) {
+
+  FWHM1 = fwhm1;
+  FWHM2 = fwhm2;
+  return true;
+}
Index: branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtraction.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtraction.h	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtraction.h	(revision 29060)
@@ -59,5 +59,5 @@
 /// Convolve the reference stamp with the kernel components
 bool pmSubtractionConvolveStamp(pmSubtractionStamp *stamp, ///< Stamp to convolve
-                                const pmSubtractionKernels *kernels, ///< Kernel parameters
+                                pmSubtractionKernels *kernels, ///< Kernel parameters
                                 int footprint ///< Half-size of region over which to calculate equation
     );
@@ -157,4 +157,7 @@
     );
 
+bool pmSubtractionGetFWHMs(float *fwhm1, float *fwhm2);
+bool pmSubtractionSetFWHMs(float fwhm1, float fwhm2);
+
 /// @}
 #endif
Index: branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtractionEquation.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtractionEquation.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtractionEquation.c	(revision 29060)
@@ -38,5 +38,6 @@
                                   const psImage *polyValues, // Spatial polynomial values
                                   int footprint, // (Half-)Size of stamp
-                                  int normWindow, // Window (half-)size for normalisation measurement
+                                  int normWindow1, // Window (half-)size for normalisation measurement
+                                  int normWindow2, // Window (half-)size for normalisation measurement
                                   const pmSubtractionEquationCalculationMode mode
                                   )
@@ -184,6 +185,8 @@
             double one = 1.0;
 
-            if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(normWindow)) {
+            if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(normWindow1)) {
                 normI1 += ref;
+            }
+            if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(normWindow2)) {
                 normI2 += in;
             }
@@ -214,4 +217,6 @@
 
     *norm = normI2 / normI1;
+
+    fprintf (stderr, "normValue: %f %f %f\n", normI1, normI2, *norm);
 
     if (mode & PM_SUBTRACTION_EQUATION_NORM) {
@@ -262,5 +267,6 @@
                                       const psImage *polyValues, // Spatial polynomial values
                                       int footprint, // (Half-)Size of stamp
-                                      int normWindow, // Window (half-)size for normalisation measurement
+                                      int normWindow1, // Window (half-)size for normalisation measurement
+                                      int normWindow2, // Window (half-)size for normalisation measurement
                                       const pmSubtractionEquationCalculationMode mode
                                       )
@@ -492,6 +498,8 @@
             double i1i2 = i1 * i2;
 
-            if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(normWindow)) {
+            if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(normWindow1)) {
                 normI1 += i1;
+            }
+            if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(normWindow2)) {
                 normI2 += i2;
             }
@@ -522,4 +530,5 @@
 
     *norm = normI2 / normI1;
+    fprintf (stderr, "normValue: %f %f %f\n", normI1, normI2, *norm);
 
     if (mode & PM_SUBTRACTION_EQUATION_NORM) {
@@ -559,8 +568,8 @@
 // Add in penalty term to least-squares vector
 bool calculatePenalty(psImage *matrix,                     // Matrix to which to add in penalty term
-                             psVector *vector,                    // Vector to which to add in penalty term
-                             const pmSubtractionKernels *kernels, // Kernel parameters
-                             float norm                           // Normalisation
-    )
+		      psVector *vector,                    // Vector to which to add in penalty term
+		      const pmSubtractionKernels *kernels, // Kernel parameters
+		      float norm                           // Normalisation
+  )
 {
     if (kernels->penalty == 0.0) {
@@ -568,5 +577,7 @@
     }
 
-    psVector *penalties = kernels->penalties; // Penalties for each kernel component
+    psVector *penalties1 = kernels->penalties1; // Penalties for each kernel component (input)
+    psVector *penalties2 = kernels->penalties2; // Penalties for each kernel component (ref)
+
     int spatialOrder = kernels->spatialOrder; // Order of spatial variations
     int numKernels = kernels->num; // Number of kernel components
@@ -588,8 +599,11 @@
             for (int xOrder = 0; xOrder <= spatialOrder - yOrder; xOrder++, index += numKernels) {
                 // Contribution to chi^2: a_i^2 P_i
-                psAssert(isfinite(penalties->data.F32[i]), "Invalid penalty");
-                matrix->data.F64[index][index] += norm * penalties->data.F32[i];
+                psAssert(isfinite(penalties1->data.F32[i]), "Invalid penalty");
+		fprintf (stderr, "penalty: %f + %f (%f * %f)\n", matrix->data.F64[index][index], norm * penalties1->data.F32[i], norm, penalties1->data.F32[i]);
+                matrix->data.F64[index][index] += norm * penalties1->data.F32[i];
                 if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
-                    matrix->data.F64[index + numParams + 2][index + numParams + 2] += norm * penalties->data.F32[i];
+		    fprintf (stderr, "penalty: (x^%d y^%d fwhm %f) : %f + %f (%f * %f)\n", kernels->u->data.S32[index], kernels->v->data.S32[index], kernels->widths->data.F32[index], 
+			     matrix->data.F64[index + numParams + 2][index + numParams + 2], norm * penalties2->data.F32[i], norm, penalties2->data.F32[i]);
+		    matrix->data.F64[index + numParams + 2][index + numParams + 2] += norm * penalties2->data.F32[i];			     
                     // matrix[i][i] is ~ (k_i * I_1)(k_i * I_1)
                     // penalties scale with second moments
@@ -682,5 +696,5 @@
 
     pmSubtractionStampList *stamps = job->args->data[0]; // List of stamps
-    const pmSubtractionKernels *kernels = job->args->data[1]; // Kernels
+    pmSubtractionKernels *kernels = job->args->data[1]; // Kernels
     int index = PS_SCALAR_VALUE(job->args->data[2], S32); // Stamp index
     pmSubtractionEquationCalculationMode mode  = PS_SCALAR_VALUE(job->args->data[3], S32); // calculation model
@@ -689,5 +703,5 @@
 }
 
-bool pmSubtractionCalculateEquationStamp(pmSubtractionStampList *stamps, const pmSubtractionKernels *kernels,
+bool pmSubtractionCalculateEquationStamp(pmSubtractionStampList *stamps, pmSubtractionKernels *kernels,
                                          int index, const pmSubtractionEquationCalculationMode mode)
 {
@@ -778,10 +792,10 @@
         status = calculateMatrixVector(stamp->matrix, stamp->vector, &stamp->norm, stamp->image2, stamp->image1,
                                        weight, window, stamp->convolutions1, kernels,
-                                       polyValues, footprint, stamps->normWindow, mode);
+                                       polyValues, footprint, stamps->normWindow1, stamps->normWindow2, mode);
         break;
       case PM_SUBTRACTION_MODE_2:
         status = calculateMatrixVector(stamp->matrix, stamp->vector, &stamp->norm, stamp->image1, stamp->image2,
                                        weight, window, stamp->convolutions2, kernels,
-                                       polyValues, footprint, stamps->normWindow, mode);
+                                       polyValues, footprint, stamps->normWindow2, stamps->normWindow1, mode);
         break;
       case PM_SUBTRACTION_MODE_DUAL:
@@ -789,5 +803,5 @@
                                            stamp->image1, stamp->image2,
                                            weight, window, stamp->convolutions1, stamp->convolutions2,
-                                           kernels, polyValues, footprint, stamps->normWindow, mode);
+                                           kernels, polyValues, footprint, stamps->normWindow1, stamps->normWindow2, mode);
         break;
       default:
@@ -830,5 +844,5 @@
 }
 
-bool pmSubtractionCalculateEquation(pmSubtractionStampList *stamps, const pmSubtractionKernels *kernels,
+bool pmSubtractionCalculateEquation(pmSubtractionStampList *stamps, pmSubtractionKernels *kernels,
                                     const pmSubtractionEquationCalculationMode mode)
 {
@@ -996,4 +1010,5 @@
             }
 
+            // double normValue = 1.0;
             double normValue = stats->robustMedian;
             // double bgValue = 0.0;
@@ -1023,4 +1038,10 @@
         }
 # endif
+
+#if (1)
+        for (int i = 0; i < solution->n; i++) {
+            fprintf(stderr, "Single solution %d: %lf\n", i, solution->data.F64[i]);
+        }
+#endif
 
         if (!kernels->solution1) {
@@ -1096,5 +1117,5 @@
 
         int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
-        calculatePenalty(sumMatrix, sumVector, kernels, sumMatrix->data.F64[normIndex][normIndex] / 1000.0);
+        calculatePenalty(sumMatrix, sumVector, kernels, sumMatrix->data.F64[normIndex][normIndex] / 100.0);
 #endif
 
@@ -1177,5 +1198,5 @@
 
 
-#ifdef TESTING
+#if (1)
         for (int i = 0; i < solution->n; i++) {
             fprintf(stderr, "Dual solution %d: %lf\n", i, solution->data.F64[i]);
Index: branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtractionEquation.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtractionEquation.h	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtractionEquation.h	(revision 29060)
@@ -19,5 +19,5 @@
 /// Calculate the least-squares equation to match the image quality for a single stamp
 bool pmSubtractionCalculateEquationStamp(pmSubtractionStampList *stamps, ///< Stamps
-                                         const pmSubtractionKernels *kernels, ///< Kernel parameters
+                                         pmSubtractionKernels *kernels, ///< Kernel parameters
                                          int index, ///< Index of stamp
                                          const pmSubtractionEquationCalculationMode mode
@@ -26,5 +26,5 @@
 /// Calculate the least-squares equation to match the image quality
 bool pmSubtractionCalculateEquation(pmSubtractionStampList *stamps, ///< Stamps
-                                    const pmSubtractionKernels *kernels, ///< Kernel parameters
+                                    pmSubtractionKernels *kernels, ///< Kernel parameters
                                     const pmSubtractionEquationCalculationMode mode
     );
Index: branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtractionKernels.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtractionKernels.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtractionKernels.c	(revision 29060)
@@ -26,5 +26,6 @@
     psFree(kernels->vStop);
     psFree(kernels->preCalc);
-    psFree(kernels->penalties);
+    psFree(kernels->penalties1);
+    psFree(kernels->penalties2);
     psFree(kernels->solution1);
     psFree(kernels->solution2);
@@ -140,5 +141,8 @@
     kernels->v = psVectorRealloc(kernels->v, start + numNew);
     kernels->preCalc = psArrayRealloc(kernels->preCalc, start + numNew);
-    kernels->penalties = psVectorRealloc(kernels->penalties, start + numNew);
+
+    kernels->penalties1 = psVectorRealloc(kernels->penalties1, start + numNew);
+    kernels->penalties2 = psVectorRealloc(kernels->penalties2, start + numNew);
+
     kernels->inner = start;
     kernels->num += numNew;
@@ -156,6 +160,12 @@
             kernels->v->data.S32[index] = v;
             kernels->preCalc->data[index] = NULL;
-            kernels->penalties->data.F32[index] = kernels->penalty * PS_SQR(PS_SQR(u) + PS_SQR(v));
-            psAssert (isfinite(kernels->penalties->data.F32[index]), "invalid penalty");
+
+	    // XXX this needs to be changed to use the *convolved* second moment
+            kernels->penalties1->data.F32[index] = kernels->penalty * PS_SQR(PS_SQR(u) + PS_SQR(v));
+            psAssert (isfinite(kernels->penalties1->data.F32[index]), "invalid penalty");
+
+            kernels->penalties2->data.F32[index] = kernels->penalty * PS_SQR(PS_SQR(u) + PS_SQR(v));
+            psAssert (isfinite(kernels->penalties2->data.F32[index]), "invalid penalty");
+
             psTrace("psModules.imcombine", 7, "Kernel %d: %d %d\n", index, u, v);
         }
@@ -166,12 +176,14 @@
     kernels->v->n = start + numNew;
     kernels->preCalc->n = start + numNew;
-    kernels->penalties->n = start + numNew;
+
+    kernels->penalties1->n = start + numNew;
+    kernels->penalties2->n = start + numNew;
 
     return true;
 }
 
-bool pmSubtractionKernelPreCalcNormalize(pmSubtractionKernels *kernels, pmSubtractionKernelPreCalc *preCalc,
-                                         int index, int size, int uOrder, int vOrder, float fwhm,
-                                         bool AlardLuptonStyle, bool forceZeroNull)
+static bool pmSubtractionKernelPreCalcNormalize(pmSubtractionKernels *kernels, pmSubtractionKernelPreCalc *preCalc,
+						int index, int uOrder, int vOrder, float fwhm,
+						bool AlardLuptonStyle, bool forceZeroNull)
 {
     // we have 4 cases here:
@@ -182,14 +194,13 @@
 
     // Calculate moments
-    double penalty = 0.0;                   // Moment, for penalty
     double sum = 0.0, sum2 = 0.0;           // Sum of kernel component
     float min = INFINITY, max = -INFINITY;  // Minimum and maximum kernel value
-    for (int v = -size; v <= size; v++) {
-        for (int u = -size; u <= size; u++) {
+
+    for (int v = preCalc->kernel->yMin; v <= preCalc->kernel->yMax; v++) {
+        for (int u = preCalc->kernel->xMin; u <= preCalc->kernel->xMax; u++) {
             double value = preCalc->kernel->kernel[v][u];
             double value2 = PS_SQR(value);
             sum += value;
             sum2 += value2;
-            penalty += value2 * PS_SQR((PS_SQR(u) + PS_SQR(v)));
             min = PS_MIN(value, min);
             max = PS_MAX(value, max);
@@ -198,5 +209,5 @@
 
 #if 0
-    fprintf(stderr, "%d raw: %lf, null: %f, min: %lf, max: %lf, moment: %lf\n", index, sum, preCalc->kernel->kernel[0][0], min, max, penalty);
+    fprintf(stderr, "%d raw: %lf, null: %f, min: %lf, max: %lf\n", index, sum, preCalc->kernel->kernel[0][0], min, max);
 #endif
 
@@ -207,5 +218,7 @@
         if (uOrder % 2 == 0 && vOrder % 2 == 0) {
             // Even functions: normalise to unit sum and subtract null pixel so that sum is zero
-            scale2D = 1.0 / fabs(sum);
+	    // Re-normalize 
+            // scale2D  = 1.0 / fabs(sum);
+            scale2D  = 1.0 / sqrt(sum2);
             zeroNull = true;
         } else {
@@ -239,22 +252,25 @@
 
     psBinaryOp(preCalc->kernel->image, preCalc->kernel->image, "*", psScalarAlloc(scale2D, PS_TYPE_F32));
-    penalty *= 1.0 / sum2;
 
     if (zeroNull) {
-        preCalc->kernel->kernel[0][0] -= 1.0;
-    }
-
-#if 0
+        // preCalc->kernel->kernel[0][0] -= 1.0;
+        preCalc->kernel->kernel[0][0] -= sum / sqrt (sum2);
+    }
+
+#if 1
     {
-        double sum = 0.0;   // Sum of kernel component
+        double Sum = 0.0;   // Sum of kernel component
+        double Sum2 = 0.0;   // Sum of kernel component
         float min = INFINITY, max = -INFINITY;  // Minimum and maximum kernel value
-        for (int v = -size; v <= size; v++) {
-            for (int u = -size; u <= size; u++) {
-                sum += preCalc->kernel->kernel[v][u];
+	for (int v = preCalc->kernel->yMin; v <= preCalc->kernel->yMax; v++) {
+	    for (int u = preCalc->kernel->xMin; u <= preCalc->kernel->xMax; u++) {
+		double value = preCalc->kernel->kernel[v][u];
+                Sum += value;
+		Sum2 += PS_SQR(value);
                 min = PS_MIN(preCalc->kernel->kernel[v][u], min);
                 max = PS_MAX(preCalc->kernel->kernel[v][u], max);
             }
         }
-        fprintf(stderr, "%d mod: %lf, null: %f, min: %lf, max: %lf, scale: %f\n", index, sum, preCalc->kernel->kernel[0][0], min, max, scale2D);
+        fprintf(stderr, "%d sum: %lf, sum2: %lf, null: %f, min: %lf, max: %lf, scale: %f\n", index, Sum, Sum2, preCalc->kernel->kernel[0][0], min, max, scale2D);
     }
 #endif
@@ -267,7 +283,5 @@
     }
     kernels->preCalc->data[index] = preCalc;
-    kernels->penalties->data.F32[index] = kernels->penalty * penalty;
-    psAssert (isfinite(kernels->penalties->data.F32[index]), "invalid penalty");
-    psTrace("psModules.imcombine", 7, "Kernel %d: %f %d %d %f\n", index, fwhm, uOrder, vOrder, penalty);
+    psTrace("psModules.imcombine", 7, "Kernel %d: %f %d %d\n", index, fwhm, uOrder, vOrder);
 
     return true;
@@ -321,6 +335,6 @@
 
                 pmSubtractionKernelPreCalc *preCalc = pmSubtractionKernelPreCalcAlloc(PM_SUBTRACTION_KERNEL_ISIS, uOrder, vOrder, size, sigma); // structure to hold precalculated values
-                pmSubtractionKernelPreCalcNormalize (kernels, preCalc, index, size, uOrder, vOrder, fwhms->data.F32[i], true, false);
-                // pmSubtractionKernelPreCalcNormalize (kernels, preCalc, index, size, uOrder, vOrder, fwhms->data.F32[i], false, false);
+                pmSubtractionKernelPreCalcNormalize (kernels, preCalc, index, uOrder, vOrder, fwhms->data.F32[i], true, false);
+                // pmSubtractionKernelPreCalcNormalize (kernels, preCalc, index, uOrder, vOrder, fwhms->data.F32[i], false, false);
             }
         }
@@ -379,5 +393,5 @@
             for (int vOrder = 0; vOrder <= orders->data.S32[i] - uOrder; vOrder++, index++) {
                 pmSubtractionKernelPreCalc *preCalc = pmSubtractionKernelPreCalcAlloc(PM_SUBTRACTION_KERNEL_ISIS, uOrder, vOrder, size, sigma); // structure to hold precalculated values
-                pmSubtractionKernelPreCalcNormalize (kernels, preCalc, index, size, uOrder, vOrder, fwhms->data.F32[i], true, false);
+                pmSubtractionKernelPreCalcNormalize (kernels, preCalc, index, uOrder, vOrder, fwhms->data.F32[i], true, false);
             }
         }
@@ -385,5 +399,5 @@
             // XXX modify size for hermitians to account for sqrt(2) in Hermitian definition (relative to ISIS Gaussian)
             pmSubtractionKernelPreCalc *preCalc = pmSubtractionKernelPreCalcAlloc(PM_SUBTRACTION_KERNEL_ISIS_RADIAL, order, order, size, sigma / sqrt(2.0)); // structure to hold precalculated values
-            pmSubtractionKernelPreCalcNormalize (kernels, preCalc, index, size, order, order, fwhms->data.F32[i], true, true);
+            pmSubtractionKernelPreCalcNormalize (kernels, preCalc, index, order, order, fwhms->data.F32[i], true, true);
         }
     }
@@ -437,5 +451,5 @@
             for (int vOrder = 0; vOrder <= orders->data.S32[i] - uOrder; vOrder++, index++) {
                 pmSubtractionKernelPreCalc *preCalc = pmSubtractionKernelPreCalcAlloc(PM_SUBTRACTION_KERNEL_HERM, uOrder, vOrder, size, sigma); // structure to hold precalculated values
-                pmSubtractionKernelPreCalcNormalize (kernels, preCalc, index, size, uOrder, vOrder, fwhms->data.F32[i], true, false);
+                pmSubtractionKernelPreCalcNormalize (kernels, preCalc, index, uOrder, vOrder, fwhms->data.F32[i], true, false);
             }
         }
@@ -506,5 +520,5 @@
 
                 // XXX do we use Alard-Lupton normalization (last param true) or not?
-                pmSubtractionKernelPreCalcNormalize (kernels, preCalc, index, size, uOrder, vOrder, fwhms->data.F32[i], true, false);
+                pmSubtractionKernelPreCalcNormalize (kernels, preCalc, index, uOrder, vOrder, fwhms->data.F32[i], true, false);
 
                 // XXXX test demo that deconvolved kernel is valid
@@ -572,5 +586,9 @@
     kernels->preCalc = psArrayAlloc(numBasisFunctions);
     kernels->penalty = penalty;
-    kernels->penalties = psVectorAlloc(numBasisFunctions, PS_TYPE_F32);
+    kernels->penalties1 = psVectorAlloc(numBasisFunctions, PS_TYPE_F32);
+    psVectorInit(kernels->penalties1, NAN);
+    kernels->penalties2 = psVectorAlloc(numBasisFunctions, PS_TYPE_F32);
+    psVectorInit(kernels->penalties2, NAN);
+    kernels->havePenalties = false;
     kernels->size = size;
     kernels->inner = 0;
@@ -771,5 +789,6 @@
 
     psWarning("Kernel penalty for dual-convolution is not configured for SPAM kernels.");
-    psVectorInit(kernels->penalties, 0.0);
+    psVectorInit(kernels->penalties1, 0.0);
+    psVectorInit(kernels->penalties2, 0.0);
 
     return kernels;
@@ -866,5 +885,6 @@
 
     psWarning("Kernel penalty for dual-convolution is not configured for FRIES kernels.");
-    psVectorInit(kernels->penalties, 0.0);
+    psVectorInit(kernels->penalties1, 0.0);
+    psVectorInit(kernels->penalties2, 0.0);
 
     return kernels;
@@ -1040,6 +1060,12 @@
                 kernels->u->data.S32[index] = uOrder;
                 kernels->v->data.S32[index] = vOrder;
-                kernels->penalties->data.F32[index] = kernels->penalty * fabsf(moment);
-                if (!isfinite(kernels->penalties->data.F32[index])) {
+
+		// XXX convert to use the convolved 2nd moment
+                kernels->penalties1->data.F32[index] = kernels->penalty * fabsf(moment);
+                if (!isfinite(kernels->penalties1->data.F32[index])) {
+                    psAbort ("invalid penalty");
+                }
+                kernels->penalties2->data.F32[index] = kernels->penalty * fabsf(moment);
+                if (!isfinite(kernels->penalties2->data.F32[index])) {
                     psAbort ("invalid penalty");
                 }
@@ -1247,5 +1273,6 @@
     out->preCalc = psMemIncrRefCounter(in->preCalc);
     out->penalty = in->penalty;
-    out->penalties = psMemIncrRefCounter(in->penalties);
+    out->penalties1 = psMemIncrRefCounter(in->penalties1);
+    out->penalties2 = psMemIncrRefCounter(in->penalties2);
     out->uStop = psMemIncrRefCounter(in->uStop);
     out->vStop = psMemIncrRefCounter(in->vStop);
Index: branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtractionKernels.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtractionKernels.h	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtractionKernels.h	(revision 29060)
@@ -39,5 +39,7 @@
     psArray *preCalc;                   ///< Array of images containing pre-calculated kernel (for ISIS, HERM or DECONV_HERM)
     float penalty;                      ///< Penalty for wideness
-    psVector *penalties;                ///< Penalty for each kernel component
+    psVector *penalties1;               ///< Penalty for each kernel component
+    psVector *penalties2;               ///< Penalty for each kernel component
+    bool havePenalties;			///< flag to test if we have already calculated the penalties or not.
     int size;                           ///< The half-size of the kernel
     int inner;                          ///< The size of an inner region
@@ -308,4 +310,3 @@
     );
 
-
 #endif
Index: branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtractionMatch.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtractionMatch.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtractionMatch.c	(revision 29060)
@@ -1304,4 +1304,7 @@
     float scale = PS_MAX(fwhm1, fwhm2) / scaleRef;      // Scaling factor
 
+    // XXX save these values in a static for later use
+    pmSubtractionSetFWHMs(fwhm1, fwhm2);
+
     if (isfinite(scaleMin) && scale < scaleMin) {
         scale = scaleMin;
Index: branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtractionMatch.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtractionMatch.h	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtractionMatch.h	(revision 29060)
@@ -110,4 +110,3 @@
     );
 
-
 #endif
Index: branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtractionStamps.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtractionStamps.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtractionStamps.c	(revision 29060)
@@ -7,18 +7,23 @@
 #include <pslib.h>
 
+#include "pmErrorCodes.h"
+#include "pmHDU.h"
+#include "pmFPA.h"
+
 // All these includes required to get stamps out of an array of pmSources
-#include "pmMoments.h"
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
-#include "pmResiduals.h"
-#include "pmHDU.h"
-#include "pmFPA.h"
-#include "pmGrowthCurve.h"
-#include "pmTrend2D.h"
-#include "pmPSF.h"
+#include "pmMoments.h"
+#include "pmModelFuncs.h"
 #include "pmModel.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
 #include "pmSource.h"
-#include "pmErrorCodes.h"
 
 #include "pmSubtraction.h"
@@ -46,5 +51,6 @@
     psFree(list->y);
     psFree(list->flux);
-    psFree(list->window);
+    psFree(list->window1);
+    psFree(list->window2);
 }
 
@@ -225,7 +231,9 @@
     list->y = NULL;
     list->flux = NULL;
-    list->window = NULL;
     list->normFrac = normFrac;
-    list->normWindow = 0;
+    list->window1 = NULL;
+    list->window2 = NULL;
+    list->normWindow1 = 0;
+    list->normWindow2 = 0;
     list->footprint = footprint;
     list->sysErr = sysErr;
@@ -248,7 +256,9 @@
     out->y = NULL;
     out->flux = NULL;
-    out->window = psMemIncrRefCounter(in->window);
+    out->window1 = psMemIncrRefCounter(in->window1);
+    out->window2 = psMemIncrRefCounter(in->window2);
     out->footprint = in->footprint;
-    out->normWindow = in->normWindow;
+    out->normWindow1 = in->normWindow1;
+    out->normWindow2 = in->normWindow2;
 
     for (int i = 0; i < num; i++) {
@@ -638,7 +648,11 @@
     int size = stamps->footprint; // Size of postage stamps
 
-    psFree (stamps->window);
-    stamps->window = psKernelAlloc(-size, size, -size, size);
-    psImageInit(stamps->window->image, 0.0);
+    psFree (stamps->window1);
+    stamps->window1 = psKernelAlloc(-size, size, -size, size);
+    psImageInit(stamps->window1->image, 0.0);
+
+    psFree (stamps->window2);
+    stamps->window2 = psKernelAlloc(-size, size, -size, size);
+    psImageInit(stamps->window2->image, 0.0);
 
     // generate normalizations for each stamp
@@ -669,6 +683,8 @@
 
     // generate the window pixels
-    double sum = 0.0;                   // Sum inside the window
-    float maxValue = 0.0;               // Maximum value, for normalisation
+    double sum1 = 0.0;                   // Sum inside the window
+    double sum2 = 0.0;                   // Sum inside the window
+    float maxValue1 = 0.0;               // Maximum value, for normalisation
+    float maxValue2 = 0.0;               // Maximum value, for normalisation
     for (int y = -size; y <= size; y++) {
         for (int x = -size; x <= size; x++) {
@@ -691,4 +707,5 @@
             }
             float f1 = stats->robustMedian;
+
             psStatsInit (stats);
             if (!psVectorStats (stats, flux2, NULL, NULL, 0)) {
@@ -697,34 +714,104 @@
             float f2 = stats->robustMedian;
 
-            stamps->window->kernel[y][x] = f1 + f2;
+            stamps->window1->kernel[y][x] = f1;
             if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(size)) {
-                sum += stamps->window->kernel[y][x];
-            }
-            maxValue = PS_MAX(maxValue, stamps->window->kernel[y][x]);
-        }
-    }
-
-    psTrace("psModules.imcombine", 3, "Window total: %f, threshold: %f\n",
-            sum, (1.0 - stamps->normFrac) * sum);
-    bool done = false;
-    for (int radius = 1; radius <= size && !done; radius++) {
-        double within = 0.0;
+                sum1 += stamps->window1->kernel[y][x];
+            }
+            maxValue1 = PS_MAX(maxValue1, stamps->window1->kernel[y][x]);
+
+            stamps->window2->kernel[y][x] = f2;
+            if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(size)) {
+                sum2 += stamps->window2->kernel[y][x];
+            }
+            maxValue2 = PS_MAX(maxValue2, stamps->window2->kernel[y][x]);
+        }
+    }
+
+    psTrace("psModules.imcombine", 3, "Window total (1): %f, threshold: %f\n", sum1, (1.0 - stamps->normFrac) * sum1);
+    psTrace("psModules.imcombine", 3, "Window total (2): %f, threshold: %f\n", sum2, (1.0 - stamps->normFrac) * sum2);
+
+# if (0)
+    // this block attempts to calculate the radius based on the first radial moment
+    bool done1 = false;
+    bool done2 = false;
+    double prior1 = 0.0;
+    double prior2 = 0.0;
+    for (int y = -size; y <= size; y++) {
+	for (int x = -size; x <= size; x++) {
+	    float r = hypot(x, y);
+	    Sr1 += r * stamps->window1->kernel[y][x];
+	    Sr2 += r * stamps->window2->kernel[y][x];
+	    Sf1 += stamps->window1->kernel[y][x];
+	    Sf2 += stamps->window2->kernel[y][x];
+        }
+    }
+    
+    float R1 = Sr1 / Sf1;
+    float R2 = Sr2 / Sf2;
+
+    stamps->normWindow1 = 2.5*R1;
+    stamps->normWindow1 = 2.5*R2;
+# else
+    // XXX : this block attempts to calculate the radius by looking at the curve of growth (or something vaguely equivalent).
+    // It did not do very well (though a true curve-of-growth analysis might be better...)
+    bool done1 = false;
+    bool done2 = false;
+    double prior1 = 0.0;
+    double prior2 = 0.0;
+    double delta1o = 1.0;
+    double delta2o = 1.0;
+    for (int radius = 1; radius <= size && !(done1 && done2); radius++) {
+        double within1 = 0.0;
+        double within2 = 0.0;
         for (int y = -radius; y <= radius; y++) {
             for (int x = -radius; x <= radius; x++) {
                 if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(radius)) {
-                    within += stamps->window->kernel[y][x];
+                    within1 += stamps->window1->kernel[y][x];
                 }
-            }
-        }
-        psTrace("psModules.imcombine", 5, "Radius %d: %f\n", radius, within);
-        if (within > (1.0 - stamps->normFrac) * sum) {
-            stamps->normWindow = radius;
-            done = true;
-        }
-    }
-
-    psTrace("psModules.imcombine", 3, "Normalisation window radius set to %d\n", stamps->normWindow);
-    if (stamps->normWindow == 0 || stamps->normWindow >= size) {
-        psError(PM_ERR_STAMPS, true, "Unable to determine normalisation window size.");
+                if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(radius)) {
+                    within2 += stamps->window2->kernel[y][x];
+                }
+            }
+        }
+	double delta1 = (within1 - prior1) / within1;
+        if (!done1 && (fabs(delta1) < stamps->normFrac)) {
+	    // interpolate to the radius at which delta2 is normFrac:
+            stamps->normWindow1 = radius - (stamps->normFrac - delta1) / (delta1o - delta1);
+	    fprintf (stderr, "choosing %f (%d) for 1 (%f : %f)\n", stamps->normWindow1, radius, within1, delta1);
+            done1 = true;
+        }
+	double delta2 = (within2 - prior2) / within2;
+        if (!done2 && (fabs(delta2) < stamps->normFrac)) {
+	    // interpolate to the radius at which delta2 is normFrac:
+            stamps->normWindow2 = radius - (stamps->normFrac - delta2) / (delta2o - delta2);
+	    fprintf (stderr, "choosing %f (%d) for 2 (%f : %f)\n", stamps->normWindow2, radius, within2, delta2);
+            done2 = true;
+        }
+        psTrace("psModules.imcombine", 5, "Radius %d: %f (%f) and %f (%f)\n", radius, within1, delta1, within2, delta2);
+
+	prior1 = within1;
+	prior2 = within2;
+	delta1o = delta1;
+	delta2o = delta2;
+
+        // if (!done1 && (within1 > (1.0 - stamps->normFrac) * sum1)) {
+        //     stamps->normWindow1 = radius;
+        //     done1 = true;
+        // }
+        // if (!done2 && (within2 > (1.0 - stamps->normFrac) * sum2)) {
+        //     stamps->normWindow2 = radius;
+        //     done2 = true;
+        // }
+
+    }
+# endif
+
+    psTrace("psModules.imcombine", 3, "Normalisation window radii set to %f and %f\n", stamps->normWindow1, stamps->normWindow2);
+    if (stamps->normWindow1 == 0 || stamps->normWindow1 >= size) {
+        psError(PM_ERR_STAMPS, true, "Unable to determine normalisation window size (1).");
+        return false;
+    }
+    if (stamps->normWindow2 == 0 || stamps->normWindow2 >= size) {
+        psError(PM_ERR_STAMPS, true, "Unable to determine normalisation window size (2).");
         return false;
     }
@@ -733,5 +820,11 @@
     for (int y = -size; y <= size; y++) {
         for (int x = -size; x <= size; x++) {
-            stamps->window->kernel[y][x] /= maxValue;
+            stamps->window1->kernel[y][x] /= maxValue1;
+        }
+    }
+    // re-normalize so chisquare values are sensible
+    for (int y = -size; y <= size; y++) {
+        for (int x = -size; x <= size; x++) {
+            stamps->window2->kernel[y][x] /= maxValue2;
         }
     }
@@ -739,6 +832,10 @@
 #if 0
     {
-        psFits *fits = psFitsOpen ("window.fits", "w");
-        psFitsWriteImage (fits, NULL, stamps->window->image, 0, NULL);
+	psFits *fits = NULL;
+	fits = psFitsOpen ("window1.fits", "w");
+        psFitsWriteImage (fits, NULL, stamps->window1->image, 0, NULL);
+        psFitsClose (fits);
+        fits = psFitsOpen ("window2.fits", "w");
+        psFitsWriteImage (fits, NULL, stamps->window2->image, 0, NULL);
         psFitsClose (fits);
     }
@@ -747,8 +844,108 @@
     psFree (stats);
     psFree (flux1);
-    psFree(flux2);
+    psFree (flux2);
     psFree (norm1);
     psFree (norm2);
     return true;
+}
+
+static pthread_mutex_t getPenaltiesMutex = PTHREAD_MUTEX_INITIALIZER;
+
+// kernels->penalty is an overall scaling factor (user-supplied)
+bool pmSubtractionKernelPenaltiesStamp(pmSubtractionStamp *stamp, pmSubtractionKernels *kernels)
+{
+    // we only need the penalties if we are doing dual convolution
+    if (kernels->mode != PM_SUBTRACTION_MODE_DUAL) return true;
+
+    // we only calculate the penalties once.
+    if (kernels->havePenalties) return true;
+
+    // in a threaded context, only one thread can calculate the penalties.  attempt to grab a
+    // mutex before continuing
+    pthread_mutex_lock(&getPenaltiesMutex);
+
+    // did someone else already get the mutex and do this?
+    if (kernels->havePenalties) {
+	pthread_mutex_unlock(&getPenaltiesMutex);
+	return true;
+    }
+
+    for (int i = 0; i < kernels->num; i++) {
+	pmSubtractionKernelPenalties(stamp, kernels, i);
+    }
+
+    kernels->havePenalties = true;
+    pthread_mutex_unlock(&getPenaltiesMutex);
+    return true;
+}
+
+# define EMPIRICAL 0
+
+// kernels->penalty is an overall scaling factor (user-supplied)
+bool pmSubtractionKernelPenalties(pmSubtractionStamp *stamp, pmSubtractionKernels *kernels, int index)
+{
+    float penalty1, penalty2;
+    float fwhm1, fwhm2;
+
+    // XXX this is annoyingly hack-ish
+    pmSubtractionGetFWHMs(&fwhm1, &fwhm2);
+
+    bool zeroNull = false;
+    int uOrder = kernels->u->data.S32[index];
+    int vOrder = kernels->v->data.S32[index];
+    if (uOrder % 2 == 0 && vOrder % 2 == 0) zeroNull = true;
+
+    if (EMPIRICAL) {
+	psKernel *convolution1 = stamp->convolutions1->data[index];
+	penalty1 = pmSubtractionKernelPenaltySingle(convolution1, zeroNull);
+
+	psKernel *convolution2 = stamp->convolutions2->data[index];
+	penalty2 = pmSubtractionKernelPenaltySingle(convolution2, zeroNull);
+    } else {
+	pmSubtractionKernelPreCalc *kernel = kernels->preCalc->data[index];
+	float M2 = pmSubtractionKernelPenaltySingle(kernel->kernel, zeroNull);
+
+	penalty1 = M2 + PS_SQR(fwhm1 / 2.35); // rescale the unconvolved second-moment by the image second moment 
+	penalty2 = M2 + PS_SQR(fwhm2 / 2.35); // rescale the unconvolved second-moment by the image second moment 
+    }
+    kernels->penalties1->data.F32[index] = kernels->penalty * penalty1;
+    psAssert (isfinite(kernels->penalties1->data.F32[index]), "invalid penalty");
+
+    kernels->penalties2->data.F32[index] = kernels->penalty * penalty2;
+    psAssert (isfinite(kernels->penalties2->data.F32[index]), "invalid penalty");
+
+    fprintf(stderr, "penalty1: %f, penalty2: %f\n", penalty1, penalty2);
+
+    return true;
+}
+
+float pmSubtractionKernelPenaltySingle(psKernel *kernel, bool zeroNull)
+{
+    // Calculate moments
+    double penalty = 0.0;                   // Moment, for penalty
+    double sum = 0.0, sum2 = 0.0;           // Sum of kernel component
+    float min = INFINITY, max = -INFINITY;  // Minimum and maximum kernel value
+    for (int v = kernel->yMin; v <= kernel->yMax; v++) {
+	for (int u = kernel->xMin; u <= kernel->xMax; u++) {
+            double value = kernel->kernel[v][u];
+	    if (false && zeroNull && (u == 0) && (v == 0)) {
+		value += 1.0;
+	    }
+            double value2 = PS_SQR(value);
+            sum += value;
+            sum2 += value2;
+            penalty += value2 * PS_SQR((PS_SQR(u) + PS_SQR(v)));
+            min = PS_MIN(value, min);
+            max = PS_MAX(value, max);
+        }
+    }
+    penalty *= 1.0 / sum2;
+
+    if (1) {
+	fprintf(stderr, "min: %lf, max: %lf, moment: %lf, flux^2: %lf\n", min, max, penalty, sum2);
+	// psTrace("psModules.imcombine", 7, "Kernel %d: %f %d %d %f\n", index, fwhm, uOrder, vOrder, penalty);
+    }
+
+    return penalty;
 }
 
Index: branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtractionStamps.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtractionStamps.h	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtractionStamps.h	(revision 29060)
@@ -24,7 +24,9 @@
     psArray *flux;                      ///< Fluxes for possible stamps (or NULL)
     int footprint;                      ///< Half-size of stamps
-    psKernel *window;                   ///< window function generated from ensemble of stamps
     float normFrac;                     ///< Fraction of flux in window for normalisation window
-    int normWindow;                     ///< Size of window for measuring normalisation
+    psKernel *window1;                  ///< window function generated from ensemble of stamps (input 1)
+    psKernel *window2;                  ///< window function generated from ensemble of stamps (input 2)
+    float normWindow1;                    ///< Size of window for measuring normalisation
+    float normWindow2;                    ///< Size of window for measuring normalisation
     float sysErr;                       ///< Systematic error
     float skyErr;                       ///< increase effective readnoise
@@ -195,3 +197,8 @@
 bool pmSubtractionStampsResetStatus (pmSubtractionStampList *stamps);
 
+
+bool pmSubtractionKernelPenaltiesStamp(pmSubtractionStamp *stamp, pmSubtractionKernels *kernels);
+bool pmSubtractionKernelPenalties(pmSubtractionStamp *stamp, pmSubtractionKernels *kernels, int index);
+float pmSubtractionKernelPenaltySingle(psKernel *kernel, bool zeroNull);
+
 #endif
Index: branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtractionVisual.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtractionVisual.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/imcombine/pmSubtractionVisual.c	(revision 29060)
@@ -21,4 +21,5 @@
 
 #include "pmVisual.h"
+#include "pmVisualUtils.h"
 
 #include "pmHDU.h"
@@ -61,5 +62,9 @@
  *    @return true for success */
 bool pmSubtractionVisualPlotConvKernels(psImage *convKernels) {
-    if (!pmVisualIsVisual() || !plotConvKernels) return true;
+
+    if (!pmVisualTestLevel("ppsub.kernels", 1)) return true;
+
+    if (!plotConvKernels) return true;
+
     if (!pmVisualInitWindow(&kapa1, "ppSub:Images")) {
         return false;
@@ -75,5 +80,9 @@
     @return true for success */
 bool pmSubtractionVisualPlotStamps(pmSubtractionStampList *stamps, pmReadout *ro) {
-    if (!pmVisualIsVisual() || !plotStamps) return true;
+
+    if (!pmVisualTestLevel("ppsub.stamps", 1)) return true;
+
+    if (!plotStamps) return true;
+
     if (!pmVisualInitWindow (&kapa1, "ppSub:Images")) {
         return false;
@@ -145,5 +154,8 @@
 bool pmSubtractionVisualPlotLeastSquares (pmSubtractionStampList *stamps, bool dual) {
 
-    if (!pmVisualIsVisual() || !plotLeastSquares) return true;
+    if (!pmVisualTestLevel("ppsub.chisq", 1)) return true;
+
+    if (!plotLeastSquares) return true;
+
     if (!pmVisualInitWindow (&kapa1, "PPSub:Images")) {
         return false;
@@ -204,5 +216,9 @@
 
 bool pmSubtractionVisualShowSubtraction(psImage *image, psImage *ref, psImage *sub) {
-    if (!pmVisualIsVisual() || !plotImage) return true;
+
+    if (!pmVisualTestLevel("ppsub.images.sub", 1)) return true;
+
+    if (!plotImage) return true;
+
     if (!pmVisualInitWindow (&kapa1, "PPSub:Images")) {
         return false;
@@ -218,5 +234,6 @@
 bool pmSubtractionVisualShowKernels(pmSubtractionKernels *kernels) {
 
-    if (!pmVisualIsVisual()) return true;
+    if (!pmVisualTestLevel("ppsub.kernels.final", 1)) return true;
+
     if (!pmVisualInitWindow (&kapa1, "PPSub:Images")) {
         return false;
@@ -264,5 +281,6 @@
 bool pmSubtractionVisualShowBasis(pmSubtractionStampList *stamps) {
 
-    if (!pmVisualIsVisual()) return true;
+    if (!pmVisualTestLevel("ppsub.basis", 1)) return true;
+
     if (!pmVisualInitWindow (&kapa2, "ppSub:StampMasterImage")) {
         return false;
@@ -425,5 +443,5 @@
 bool pmSubtractionVisualShowFitInit(pmSubtractionStampList *stamps) {
 
-    if (!pmVisualIsVisual()) return true;
+    if (!pmVisualTestLevel("ppsub.fit", 1)) return true;
 
     // generate 4 storage images large enough to hold the N stamps:
@@ -462,5 +480,5 @@
 bool pmSubtractionVisualShowFitAddStamp(psKernel *target, psKernel *source, psKernel *convolution, double background, double norm, int index) {
 
-    if (!pmVisualIsVisual()) return true;
+    if (!pmVisualTestLevel("ppsub.stamp", 1)) return true;
 
     double sum;
@@ -543,5 +561,6 @@
     }
 
-    if (!pmVisualIsVisual()) return true;
+    if (!pmVisualTestLevel("ppsub.fit", 1)) return true;
+
     if (!pmVisualInitWindow(&kapa1, "ppSub:Images")) return false;
     if (!pmVisualInitWindow(&kapa2, "ppSub:Misc")) return false;
@@ -605,5 +624,6 @@
     Graphdata graphdata;
 
-    if (!pmVisualIsVisual()) return true;
+    if (!pmVisualTestLevel("ppsub.fit", 1)) return true;
+
     if (!pmVisualInitWindow(&kapa3, "ppSub:plots")) return false;
 
Index: branches/sc_branches/trunkTest/psModules/src/objects/Makefile.am
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/Makefile.am	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/Makefile.am	(revision 29060)
@@ -43,6 +43,8 @@
 	pmSourceIO_CMF_PS1_V1.c \
 	pmSourceIO_CMF_PS1_V2.c \
+	pmSourceIO_CMF_PS1_V3.c \
 	pmSourceIO_CMF_PS1_SV1.c \
 	pmSourceIO_CMF_PS1_DV1.c \
+	pmSourceIO_CMF_PS1_DV2.c \
 	pmSourceIO_MatchedRefs.c \
 	pmSourcePlots.c \
@@ -60,4 +62,7 @@
 	pmPSFtryFitPSF.c \
 	pmPSFtryMetric.c \
+	pmPCMdata.c \
+	pmPCM_MinimizeChisq.c \
+	pmSourceFitPCM.c \
 	pmTrend2D.c \
 	pmGrowthCurveGenerate.c \
@@ -71,5 +76,7 @@
 	models/pmModel_QGAUSS.c \
 	models/pmModel_RGAUSS.c \
-	models/pmModel_SERSIC.c
+	models/pmModel_SERSIC.c \
+	models/pmModel_EXP.c \
+	models/pmModel_DEV.c
 
 pkginclude_HEADERS = \
@@ -80,4 +87,5 @@
 	pmPeaks.h \
 	pmMoments.h \
+	pmModelFuncs.h \
 	pmModel.h \
 	pmModelClass.h \
@@ -101,4 +109,5 @@
 	pmPSF_IO.h \
 	pmPSFtry.h \
+	pmPCMdata.h \
 	pmTrend2D.h \
 	pmGrowthCurve.h \
@@ -111,5 +120,7 @@
 	models/pmModel_QGAUSS.h \
 	models/pmModel_RGAUSS.h \
-	models/pmModel_SERSIC.h
+	models/pmModel_SERSIC.h \
+	models/pmModel_EXP.h \
+	models/pmModel_DEV.h
 
 CLEANFILES = *~
Index: branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_DEV.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_DEV.c	(revision 29060)
+++ branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_DEV.c	(revision 29060)
@@ -0,0 +1,472 @@
+/******************************************************************************
+ * this file defines the DEV source shape model.  Note that these model functions are loaded
+ * by pmModelClass.c using 'include', and thus need no 'include' statements of their own.  The
+ * models use a psVector to represent the set of parameters, with the sequence used to specify
+ * the meaning of the parameter.  The meaning of the parameters may thus vary depending on the
+ * specifics of the model.  All models which are used as a PSF representations share a few
+ * parameters, for which # define names are listed in pmModel.h:
+
+   f = exp(-z^n)
+
+   * PM_PAR_SKY 0   - local sky : note that this is unused and may be dropped in the future
+   * PM_PAR_I0 1    - central intensity
+   * PM_PAR_XPOS 2  - X center of object
+   * PM_PAR_YPOS 3  - Y center of object
+   * PM_PAR_SXX 4   - X^2 term of elliptical contour (sqrt(2) / SigmaX)
+   * PM_PAR_SYY 5   - Y^2 term of elliptical contour (sqrt(2) / SigmaY)
+   * PM_PAR_SXY 6   - X*Y term of elliptical contour
+   * PM_PAR_7   7   - normalized dev parameter
+
+   note that a standard dev model uses exp(-K*(z^(1/2n) - 1).  the additional elements (K,
+   the -1 offset) are absorbed in this model by the normalization, the exponent, and the
+   radial scale.  We fit the elements in this form, then re-normalize them on output.
+   *****************************************************************************/
+
+#include <stdio.h>
+#include <pslib.h>
+#include "pmHDU.h"
+#include "pmFPA.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmSpan.h"
+#include "pmFootprintSpans.h"
+#include "pmFootprint.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
+#include "pmPSF.h"
+#include "pmPSFtry.h"
+#include "pmDetections.h"
+
+#include "pmModel_DEV.h"
+
+# define PM_MODEL_NPARAM          7
+# define PM_MODEL_FUNC            pmModelFunc_DEV
+# define PM_MODEL_FLUX            pmModelFlux_DEV
+# define PM_MODEL_GUESS           pmModelGuess_DEV
+# define PM_MODEL_LIMITS          pmModelLimits_DEV
+# define PM_MODEL_RADIUS          pmModelRadius_DEV
+# define PM_MODEL_FROM_PSF        pmModelFromPSF_DEV
+# define PM_MODEL_PARAMS_FROM_PSF pmModelParamsFromPSF_DEV
+# define PM_MODEL_FIT_STATUS      pmModelFitStatus_DEV
+# define PM_MODEL_SET_LIMITS      pmModelSetLimits_DEV
+
+// f = exp(-z^0.125) 
+# define ALPHA 0.125 
+// # define ALPHA 0.25 
+
+// the model is a function of the pixel coordinate (pixcoord[0,1] = x,y)
+// 0.5 PIX: the parameters are defined in terms of pixel coords, so the incoming pixcoords
+// values need to be pixel coords
+
+// Lax parameter limits
+static float paramsMinLax[] = { -1.0e3, 1.0e-2, -100, -100, 0.001, 0.001, -1.0 };
+static float paramsMaxLax[] = { 1.0e5, 1.0e8, 1.0e4, 1.0e4, 100, 100, 1.0 };
+
+// Moderate parameter limits
+static float *paramsMinModerate = paramsMinLax;
+static float *paramsMaxModerate = paramsMaxLax;
+
+// Strict parameter limits
+static float *paramsMinStrict = paramsMinLax;
+static float *paramsMaxStrict = paramsMaxLax;
+
+// Parameter limits to use
+static float *paramsMinUse = paramsMinLax;
+static float *paramsMaxUse = paramsMaxLax;
+static float betaUse[] = { 1000, 3e6, 5, 5, 1.0, 1.0, 0.5 };
+
+static bool limitsApply = true;         // Apply limits?
+
+psF32 PM_MODEL_FUNC (psVector *deriv,
+                     const psVector *params,
+                     const psVector *pixcoord)
+{
+    psF32 *PAR = params->data.F32;
+
+    float index = 0.5 / ALPHA;
+    float bn = 1.9992*index - 0.3271;
+    float Io = exp(bn);
+
+    psF32 X  = pixcoord->data.F32[0] - PAR[PM_PAR_XPOS];
+    psF32 Y  = pixcoord->data.F32[1] - PAR[PM_PAR_YPOS];
+    psF32 px = X / PAR[PM_PAR_SXX];
+    psF32 py = Y / PAR[PM_PAR_SYY];
+    psF32 z  = (PS_SQR(px) + PS_SQR(py) + PAR[PM_PAR_SXY]*X*Y);
+
+    assert (z >= 0);
+
+    psF32 f2 = bn*pow(z,ALPHA);
+    psF32 f1 = Io*exp(-f2);
+    psF32 z0 = PAR[PM_PAR_I0]*f1;
+    psF32 f0 = PAR[PM_PAR_SKY] + z0;
+
+    assert (isfinite(f2));
+    assert (isfinite(f1));
+    assert (isfinite(z0));
+    assert (isfinite(f0));
+
+    if (deriv != NULL) {
+        psF32 *dPAR = deriv->data.F32;
+
+        // gradient is infinite for z = 0; saturate at z = 0.01
+        psF32 z1 = (z < 0.01) ? z0*bn*ALPHA*pow(0.01,ALPHA - 1.0) : z0*bn*ALPHA*pow(z,ALPHA - 1.0);
+
+        dPAR[PM_PAR_SKY]  = +1.0;
+        dPAR[PM_PAR_I0]   = +2.0*f1;
+
+        assert (isfinite(z1));
+
+        dPAR[PM_PAR_XPOS] = +1.0*z1*(2.0*px/PAR[PM_PAR_SXX] + Y*PAR[PM_PAR_SXY]);
+        dPAR[PM_PAR_YPOS] = +1.0*z1*(2.0*py/PAR[PM_PAR_SYY] + X*PAR[PM_PAR_SXY]);
+        dPAR[PM_PAR_SXX]  = +2.0*z1*px*px/PAR[PM_PAR_SXX];
+        dPAR[PM_PAR_SYY]  = +2.0*z1*py*py/PAR[PM_PAR_SYY];
+        dPAR[PM_PAR_SXY]  = -1.0*z1*X*Y;
+    }
+    return (f0);
+}
+
+// define the parameter limits
+// AR_MAX is the maximum allowed axis ratio
+// AR_RATIO is ((1-R)/(1+R))^2 where R = AR_MAX^(-2)
+# define AR_MAX 20.0
+# define AR_RATIO 0.99
+bool PM_MODEL_LIMITS (psMinConstraintMode mode, int nParam, float *params, float *beta)
+{
+    if (!limitsApply) {
+        return true;
+    }
+    psAssert(nParam >= 0 && nParam < PM_MODEL_NPARAM, "Parameter index is out of bounds");
+
+    // we need to calculate the limits for SXY specially
+    float q2 = NAN;
+    if (nParam == PM_PAR_SXY) {
+        float f1 = 1.0 / PS_SQR(params[PM_PAR_SYY]) + 1.0 / PS_SQR(params[PM_PAR_SXX]);
+        float f2 = 1.0 / PS_SQR(params[PM_PAR_SYY]) - 1.0 / PS_SQR(params[PM_PAR_SXX]);
+        float q1 = PS_SQR(f1)*AR_RATIO - PS_SQR(f2);
+        q1 = (q1 < 0.0) ? 0.0 : q1;
+        // if q1 < 0.0, f2 ~ f1, we have a very large axis ratio near 45deg..  Saturate at that
+        // angle and let f2,f1 fight it out
+        q2 = 0.5*sqrtf(q1);
+    }
+
+    switch (mode) {
+      case PS_MINIMIZE_BETA_LIMIT: {
+          psAssert(beta, "Require beta to limit beta");
+          float limit = betaUse[nParam];
+          if (nParam == PM_PAR_SXY) {
+              limit *= q2;
+          }
+          if (fabs(beta[nParam]) > fabs(limit)) {
+              beta[nParam] = (beta[nParam] > 0) ? fabs(limit) : -fabs(limit);
+              psTrace("psModules.objects", 5, "|beta[nParam==%d]| > |beta_lim|; %g v. %g",
+                      nParam, beta[nParam], limit);
+              return false;
+          }
+          return true;
+      }
+      case PS_MINIMIZE_PARAM_MIN: {
+          psAssert(params, "Require parameters to limit parameters");
+          psAssert(paramsMinUse, "Require parameter limits to limit parameters");
+          float limit = paramsMinUse[nParam];
+          if (nParam == PM_PAR_SXY) {
+              limit *= q2;
+          }
+          if (params[nParam] < limit) {
+              params[nParam] = limit;
+              psTrace("psModules.objects", 5, "params[nParam==%d] < params_min; %g v. %g",
+                      nParam, params[nParam], limit);
+              return false;
+          }
+          return true;
+      }
+      case PS_MINIMIZE_PARAM_MAX: {
+          psAssert(params, "Require parameters to limit parameters");
+          psAssert(paramsMaxUse, "Require parameter limits to limit parameters");
+          float limit = paramsMaxUse[nParam];
+          if (nParam == PM_PAR_SXY) {
+              limit *= q2;
+          }
+          if (params[nParam] > limit) {
+              params[nParam] = limit;
+              psTrace("psModules.objects", 5, "params[nParam==%d] > params_max; %g v. %g",
+                      nParam, params[nParam], limit);
+              return false;
+          }
+          return true;
+      }
+      default:
+        psAbort("invalid choice for limits");
+    }
+    psAbort("should not reach here");
+    return false;
+}
+
+// make an initial guess for parameters
+// 0.5 PIX: moments and peaks are in pixel coords, thus so are model parameters
+bool PM_MODEL_GUESS (pmModel *model, pmSource *source)
+{
+    pmMoments *moments = source->moments;
+    pmPeak    *peak    = source->peak;
+    psF32     *PAR  = model->params->data.F32;
+
+    psEllipseMoments emoments;
+    emoments.x2 = moments->Mxx;
+    emoments.xy = moments->Mxy;
+    emoments.y2 = moments->Myy;
+
+    // force the axis ratio to be < 20.0
+    psEllipseAxes axes = psEllipseMomentsToAxes (emoments, 20.0);
+
+    if (!isfinite(axes.major)) return false;
+    if (!isfinite(axes.minor)) return false;
+    if (!isfinite(axes.theta)) return false;
+
+    psEllipseShape shape = psEllipseAxesToShape (axes);
+
+    if (!isfinite(shape.sx))  return false;
+    if (!isfinite(shape.sy))  return false;
+    if (!isfinite(shape.sxy)) return false;
+
+    // the other parameters depend on the guess for PAR_7
+    float index = 0.5 / ALPHA;
+    float bn = 1.9992*index - 0.3271;
+    // float fR = 1.0 / (sqrt(2.0) * pow (bn, index));
+    float Io = exp(0.5*bn);
+
+    float Sxx = PS_MAX(0.5, shape.sx);
+    float Syy = PS_MAX(0.5, shape.sy);
+
+    PAR[PM_PAR_SKY]  = 0.0;
+    PAR[PM_PAR_I0]   = peak->flux / Io;
+    PAR[PM_PAR_XPOS] = peak->xf;
+    PAR[PM_PAR_YPOS] = peak->yf;
+    // PAR[PM_PAR_SXX]  = Sxx * fR;
+    // PAR[PM_PAR_SYY]  = Syy * fR;
+    PAR[PM_PAR_SXX]  = Sxx;
+    PAR[PM_PAR_SYY]  = Syy;
+    PAR[PM_PAR_SXY]  = shape.sxy;
+
+    return(true);
+}
+
+psF64 PM_MODEL_FLUX (const psVector *params)
+{
+    float z, norm;
+    psEllipseShape shape;
+
+    psF32 *PAR = params->data.F32;
+
+    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
+    shape.sy  = PAR[PM_PAR_SYY] / M_SQRT2;
+    shape.sxy = PAR[PM_PAR_SXY];
+
+    // Area is equivalent to 2 pi sigma^2
+    psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
+    psF64 Area = 2.0 * M_PI * axes.major * axes.minor;
+
+    // the area needs to be multiplied by the integral of f(z)
+    norm = 0.0;
+
+    # define DZ 0.25
+
+    float f0 = 1.0;
+    float f1, f2;
+    for (z = DZ; z < 50; z += DZ) {
+	f1 = exp(-pow(z,ALPHA));
+        z += DZ;
+	f2 = exp(-pow(z,ALPHA));
+        norm += f0 + 4*f1 + f2;
+        f0 = f2;
+    }
+    norm *= DZ / 3.0;
+
+    psF64 Flux = PAR[PM_PAR_I0] * Area * norm;
+
+    return(Flux);
+}
+
+// define this function so it never returns Inf or NaN
+// return the radius which yields the requested flux
+psF64 PM_MODEL_RADIUS (const psVector *params, psF64 flux)
+{
+    psEllipseShape shape;
+
+    psF32 *PAR = params->data.F32;
+
+    if (flux <= 0)
+        return (1.0);
+    if (PAR[PM_PAR_I0] <= 0)
+        return (1.0);
+    if (flux >= PAR[PM_PAR_I0])
+        return (1.0);
+
+    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
+    shape.sy  = PAR[PM_PAR_SYY] / M_SQRT2;
+    shape.sxy = PAR[PM_PAR_SXY];
+
+    psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
+
+    // f = Io exp(-z^n) -> z^n = ln(Io/f)
+    psF64 zn = log(PAR[PM_PAR_I0] / flux);
+    psF64 radius = axes.major * sqrt (2.0) * pow(zn, 0.5 / ALPHA);
+
+    psAssert (isfinite(radius), "fix this code: z should not be nan");
+    return (radius);
+}
+
+bool PM_MODEL_FROM_PSF (pmModel *modelPSF, pmModel *modelFLT, const pmPSF *psf)
+{
+
+    psF32 *out = modelPSF->params->data.F32;
+    psF32 *in  = modelFLT->params->data.F32;
+
+    // we require these two parameters to exist
+    assert (psf->params->n > PM_PAR_YPOS);
+    assert (psf->params->n > PM_PAR_XPOS);
+
+    for (int i = 0; i < psf->params->n; i++) {
+        if (psf->params->data[i] == NULL) {
+            out[i] = in[i];
+        } else {
+            pmTrend2D *trend = psf->params->data[i];
+            out[i] = pmTrend2DEval(trend, in[PM_PAR_XPOS], in[PM_PAR_YPOS]);
+        }
+    }
+
+    // the 2D PSF model fits polarization terms (E0,E1,E2)
+    // convert to shape terms (SXX,SYY,SXY)
+    if (!pmPSF_FitToModel (out, 0.1)) {
+        psTrace("psModules.objects", 5, "Failed to fit object at (r,c) = (%.1f,%.1f)", in[PM_PAR_YPOS], in[PM_PAR_XPOS]);
+        return false;
+    }
+
+    // apply the model limits here: this truncates excessive extrapolation
+    // XXX do we need to do this still?  should we put in asserts to test?
+    for (int i = 0; i < psf->params->n; i++) {
+        // apply the limits to all components or just the psf-model parameters?
+        if (psf->params->data[i] == NULL)
+            continue;
+
+        bool status = true;
+        status &= PM_MODEL_LIMITS(PS_MINIMIZE_PARAM_MIN, i, out, NULL);
+        status &= PM_MODEL_LIMITS(PS_MINIMIZE_PARAM_MAX, i, out, NULL);
+        if (!status) {
+            psTrace ("psModules.objects", 5, "Hitting parameter limits at (r,c) = (%.1f, %.1f)",
+                     in[PM_PAR_XPOS], in[PM_PAR_YPOS]);
+            modelPSF->flags |= PM_MODEL_STATUS_LIMITS;
+        }
+    }
+
+    return true;
+}
+
+// construct the PSF model from the FLT model and the psf
+// XXX is this sufficiently general do be a global function, not a pmModelClass function?
+bool PM_MODEL_PARAMS_FROM_PSF (pmModel *model, const pmPSF *psf, float Xo, float Yo, float Io)
+{
+    psF32 *PAR = model->params->data.F32;
+
+    // we require these two parameters to exist
+    assert (psf->params->n > PM_PAR_YPOS);
+    assert (psf->params->n > PM_PAR_XPOS);
+
+    PAR[PM_PAR_SKY]  = 0.0;
+    PAR[PM_PAR_I0]   = Io;
+    PAR[PM_PAR_XPOS] = Xo;
+    PAR[PM_PAR_YPOS] = Yo;
+
+    // supply the model-fitted parameters, or copy from the input
+    for (int i = 0; i < psf->params->n; i++) {
+        if (i == PM_PAR_SKY) continue;
+        if (i == PM_PAR_I0) continue;
+        if (i == PM_PAR_XPOS) continue;
+        if (i == PM_PAR_YPOS) continue;
+        pmTrend2D *trend = psf->params->data[i];
+        PAR[i] = pmTrend2DEval(trend, Xo, Yo);
+    }
+
+    // the 2D PSF model fits polarization terms (E0,E1,E2)
+    // convert to shape terms (SXX,SYY,SXY)
+    // XXX user-defined value for limit?
+    if (!pmPSF_FitToModel (PAR, 0.1)) {
+        psTrace ("psModules.objects", 3, "Failed to fit object at (r,c) = (%.1f,%.1f)", Xo, Yo);
+        return false;
+    }
+
+    // apply the model limits here: this truncates excessive extrapolation
+    // XXX do we need to do this still?  should we put in asserts to test?
+    for (int i = 0; i < psf->params->n; i++) {
+        // apply the limits to all components or just the psf-model parameters?
+        if (psf->params->data[i] == NULL)
+            continue;
+
+        bool status = true;
+        status &= PM_MODEL_LIMITS (PS_MINIMIZE_PARAM_MIN, i, PAR, NULL);
+        status &= PM_MODEL_LIMITS (PS_MINIMIZE_PARAM_MAX, i, PAR, NULL);
+        if (!status) {
+            psTrace ("psModules.objects", 5, "Hitting parameter limits at (r,c) = (%.1f, %.1f)", Xo, Yo);
+            model->flags |= PM_MODEL_STATUS_LIMITS;
+        }
+    }
+    return(true);
+}
+
+bool PM_MODEL_FIT_STATUS (pmModel *model)
+{
+    bool  status;
+
+    psF32 *PAR  = model->params->data.F32;
+    psF32 *dPAR = model->dparams->data.F32;
+
+    status = true;
+    status &= (PAR[PM_PAR_I0] > 0);
+    status &= ((dPAR[PM_PAR_I0]/PAR[PM_PAR_I0]) < 0.5);
+
+    return status;
+}
+
+
+void PM_MODEL_SET_LIMITS(pmModelLimitsType type)
+{
+    switch (type) {
+      case PM_MODEL_LIMITS_NONE:
+        paramsMinUse = NULL;
+        paramsMaxUse = NULL;
+        limitsApply = true;
+        break;
+      case PM_MODEL_LIMITS_IGNORE:
+        paramsMinUse = NULL;
+        paramsMaxUse = NULL;
+        limitsApply = false;
+        break;
+      case PM_MODEL_LIMITS_LAX:
+        paramsMinUse = paramsMinLax;
+        paramsMaxUse = paramsMaxLax;
+        limitsApply = true;
+        break;
+      case PM_MODEL_LIMITS_MODERATE:
+        paramsMinUse = paramsMinModerate;
+        paramsMaxUse = paramsMaxModerate;
+        limitsApply = true;
+        break;
+      case PM_MODEL_LIMITS_STRICT:
+        paramsMinUse = paramsMinStrict;
+        paramsMaxUse = paramsMaxStrict;
+        limitsApply = true;
+        break;
+      default:
+        psAbort("Unrecognised model limits type: %x", type);
+    }
+    return;
+}
Index: branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_DEV.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_DEV.h	(revision 29060)
+++ branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_DEV.h	(revision 29060)
@@ -0,0 +1,15 @@
+#ifndef PM_MODEL_DEV_H
+
+#include "pmModel.h"
+
+psF32 pmModelFunc_DEV(psVector *deriv, const psVector *params, const psVector *pixcoord);
+bool pmModelLimits_DEV(psMinConstraintMode mode, int nParam, float *params, float *beta);
+bool pmModelGuess_DEV(pmModel *model, pmSource *source);
+psF64 pmModelFlux_DEV(const psVector *params);
+psF64 pmModelRadius_DEV(const psVector *params, psF64 flux);
+bool pmModelFromPSF_DEV(pmModel *modelPSF, pmModel *modelFLT, const pmPSF *psf);
+bool  pmModelParamsFromPSF_DEV(pmModel *model, const pmPSF *psf, float Xo, float Yo, float Io);
+bool pmModelFitStatus_DEV(pmModel *model);
+void pmModelSetLimits_DEV(pmModelLimitsType type);
+
+#endif
Index: branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_EXP.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_EXP.c	(revision 29060)
+++ branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_EXP.c	(revision 29060)
@@ -0,0 +1,453 @@
+/******************************************************************************
+ * this file defines the Exponential (EXP) source shape model.  Note that these model functions
+ * are loaded by pmModelClass.c using 'include', and thus need no 'include' statements of their
+ * own.  The models use a psVector to represent the set of parameters, with the sequence used
+ * to specify the meaning of the parameter.  The meaning of the parameters may thus vary
+ * depending on the specifics of the model.  All models which are used as a PSF representations
+ * share a few parameters, for which # define names are listed in pmModel.h:
+
+   f = exp(-sqrt(z)) (since z is r^2)
+
+   * PM_PAR_SKY 0   - local sky : note that this is unused and may be dropped in the future
+   * PM_PAR_I0 1    - central intensity
+   * PM_PAR_XPOS 2  - X center of object
+   * PM_PAR_YPOS 3  - Y center of object
+   * PM_PAR_SXX 4   - X^2 term of elliptical contour (sqrt(2) / SigmaX)
+   * PM_PAR_SYY 5   - Y^2 term of elliptical contour (sqrt(2) / SigmaY)
+   * PM_PAR_SXY 6   - X*Y term of elliptical contour
+
+   *****************************************************************************/
+
+#include <stdio.h>
+#include <pslib.h>
+#include "pmHDU.h"
+#include "pmFPA.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmSpan.h"
+#include "pmFootprintSpans.h"
+#include "pmFootprint.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
+#include "pmPSF.h"
+#include "pmPSFtry.h"
+#include "pmDetections.h"
+
+#include "pmModel_EXP.h"
+
+# define PM_MODEL_NPARAM          7
+# define PM_MODEL_FUNC            pmModelFunc_EXP
+# define PM_MODEL_FLUX            pmModelFlux_EXP
+# define PM_MODEL_GUESS           pmModelGuess_EXP
+# define PM_MODEL_LIMITS          pmModelLimits_EXP
+# define PM_MODEL_RADIUS          pmModelRadius_EXP
+# define PM_MODEL_FROM_PSF        pmModelFromPSF_EXP
+# define PM_MODEL_PARAMS_FROM_PSF pmModelParamsFromPSF_EXP
+# define PM_MODEL_FIT_STATUS      pmModelFitStatus_EXP
+# define PM_MODEL_SET_LIMITS      pmModelSetLimits_EXP
+
+// the model is a function of the pixel coordinate (pixcoord[0,1] = x,y)
+// 0.5 PIX: the parameters are defined in terms of pixel coords, so the incoming pixcoords
+// values need to be pixel coords
+
+// Lax parameter limits
+static float paramsMinLax[] = { -1.0e3, 1.0e-2, -100, -100, 0.05, 0.05, -1.0 };
+static float paramsMaxLax[] = { 1.0e5, 1.0e8, 1.0e4, 1.0e4, 100, 100, 1.0 };
+
+// Moderate parameter limits
+static float *paramsMinModerate = paramsMinLax;
+static float *paramsMaxModerate = paramsMaxLax;
+
+// Strict parameter limits
+static float *paramsMinStrict = paramsMinLax;
+static float *paramsMaxStrict = paramsMaxLax;
+
+// Parameter limits to use
+static float *paramsMinUse = paramsMinLax;
+static float *paramsMaxUse = paramsMaxLax;
+static float betaUse[] = { 1000, 3e6, 5, 5, 1.0, 1.0, 0.5};
+
+static bool limitsApply = true;         // Apply limits?
+
+psF32 PM_MODEL_FUNC (psVector *deriv,
+                     const psVector *params,
+                     const psVector *pixcoord)
+{
+    psF32 *PAR = params->data.F32;
+
+    psF32 X  = pixcoord->data.F32[0] - PAR[PM_PAR_XPOS];
+    psF32 Y  = pixcoord->data.F32[1] - PAR[PM_PAR_YPOS];
+    psF32 px = X / PAR[PM_PAR_SXX];
+    psF32 py = Y / PAR[PM_PAR_SYY];
+    psF32 z  = PS_SQR(px) + PS_SQR(py) + PAR[PM_PAR_SXY]*X*Y;
+
+    // XXX if the elliptical contour is defined in valid way, this step should not be required.
+    // other models (like PGAUSS) don't use fractional powers, and thus do not have NaN values
+    // for negative values of z
+    // XXX use an assert here to force the elliptical parameters to be correctly determined
+    // if (z < 0) z = 0;
+    assert (z >= 0);
+
+    psF32 f2 = sqrt(z);
+    psF32 f1 = exp(-f2);
+    psF32 z0 = PAR[PM_PAR_I0]*f1;
+    psF32 f0 = PAR[PM_PAR_SKY] + z0;
+
+    assert (isfinite(f2));
+    assert (isfinite(f1));
+    assert (isfinite(z0));
+    assert (isfinite(f0));
+
+    if (deriv != NULL) {
+        psF32 *dPAR = deriv->data.F32;
+
+        dPAR[PM_PAR_SKY]  = +1.0;
+        dPAR[PM_PAR_I0]   = +f1;
+
+        // gradient is infinite for z = 0; saturate at z = 0.01
+	// z1 is -df/dz (the negative sign is canceled by most of dz/dPAR[i]
+        psF32 z1 = (z < 0.01) ? 0.5*z0/sqrt(0.01) : 0.5*z0/sqrt(z);
+
+	// XXX dampen SXX and SYY as in GAUSS?
+        dPAR[PM_PAR_XPOS] = +1.0*z1*(2.0*px/PAR[PM_PAR_SXX] + Y*PAR[PM_PAR_SXY]);
+        dPAR[PM_PAR_YPOS] = +1.0*z1*(2.0*py/PAR[PM_PAR_SYY] + X*PAR[PM_PAR_SXY]);
+        dPAR[PM_PAR_SXX]  = +2.0*z1*px*px/PAR[PM_PAR_SXX];
+        dPAR[PM_PAR_SYY]  = +2.0*z1*py*py/PAR[PM_PAR_SYY];
+        dPAR[PM_PAR_SXY]  = -1.0*z1*X*Y;
+    }
+    return (f0);
+}
+
+// define the parameter limits
+// AR_MAX is the maximum allowed axis ratio
+// AR_RATIO is ((1-R)/(1+R))^2 where R = AR_MAX^(-2)
+# define AR_MAX 20.0
+# define AR_RATIO 0.99
+bool PM_MODEL_LIMITS (psMinConstraintMode mode, int nParam, float *params, float *beta)
+{
+    if (!limitsApply) {
+        return true;
+    }
+    psAssert(nParam >= 0 && nParam < PM_MODEL_NPARAM, "Parameter index is out of bounds");
+
+    // we need to calculate the limits for SXY specially
+    float q2 = NAN;
+    if (nParam == PM_PAR_SXY) {
+        float f1 = 1.0 / PS_SQR(params[PM_PAR_SYY]) + 1.0 / PS_SQR(params[PM_PAR_SXX]);
+        float f2 = 1.0 / PS_SQR(params[PM_PAR_SYY]) - 1.0 / PS_SQR(params[PM_PAR_SXX]);
+        float q1 = PS_SQR(f1)*AR_RATIO - PS_SQR(f2);
+        q1 = (q1 < 0.0) ? 0.0 : q1;
+        // if q1 < 0.0, f2 ~ f1, we have a very large axis ratio near 45deg..  Saturate at that
+        // angle and let f2,f1 fight it out
+        q2 = 0.5*sqrtf(q1);
+    }
+
+    switch (mode) {
+      case PS_MINIMIZE_BETA_LIMIT: {
+          psAssert(beta, "Require beta to limit beta");
+          float limit = betaUse[nParam];
+          if (nParam == PM_PAR_SXY) {
+              limit *= q2;
+          }
+          if (fabs(beta[nParam]) > fabs(limit)) {
+              beta[nParam] = (beta[nParam] > 0) ? fabs(limit) : -fabs(limit);
+              psTrace("psModules.objects", 5, "|beta[nParam==%d]| > |beta_lim|; %g v. %g",
+                      nParam, beta[nParam], limit);
+              return false;
+          }
+          return true;
+      }
+      case PS_MINIMIZE_PARAM_MIN: {
+          psAssert(params, "Require parameters to limit parameters");
+          psAssert(paramsMinUse, "Require parameter limits to limit parameters");
+          float limit = paramsMinUse[nParam];
+          if (nParam == PM_PAR_SXY) {
+              limit *= q2;
+          }
+          if (params[nParam] < limit) {
+              params[nParam] = limit;
+              psTrace("psModules.objects", 5, "params[nParam==%d] < params_min; %g v. %g",
+                      nParam, params[nParam], limit);
+              return false;
+          }
+          return true;
+      }
+      case PS_MINIMIZE_PARAM_MAX: {
+          psAssert(params, "Require parameters to limit parameters");
+          psAssert(paramsMaxUse, "Require parameter limits to limit parameters");
+          float limit = paramsMaxUse[nParam];
+          if (nParam == PM_PAR_SXY) {
+              limit *= q2;
+          }
+          if (params[nParam] > limit) {
+              params[nParam] = limit;
+              psTrace("psModules.objects", 5, "params[nParam==%d] > params_max; %g v. %g",
+                      nParam, params[nParam], limit);
+              return false;
+          }
+          return true;
+      }
+      default:
+        psAbort("invalid choice for limits");
+    }
+    psAbort("should not reach here");
+    return false;
+}
+
+// make an initial guess for parameters
+// 0.5 PIX: moments and peaks are in pixel coords, thus so are model parameters
+bool PM_MODEL_GUESS (pmModel *model, pmSource *source)
+{
+    pmMoments *moments = source->moments;
+    pmPeak    *peak    = source->peak;
+    psF32     *PAR  = model->params->data.F32;
+
+    psEllipseMoments emoments;
+    emoments.x2 = moments->Mxx;
+    emoments.xy = moments->Mxy;
+    emoments.y2 = moments->Myy;
+
+    // force the axis ratio to be < 20.0
+    psEllipseAxes axes = psEllipseMomentsToAxes (emoments, 20.0);
+
+    if (!isfinite(axes.major)) return false;
+    if (!isfinite(axes.minor)) return false;
+    if (!isfinite(axes.theta)) return false;
+
+    psEllipseShape shape = psEllipseAxesToShape (axes);
+
+    if (!isfinite(shape.sx))  return false;
+    if (!isfinite(shape.sy))  return false;
+    if (!isfinite(shape.sxy)) return false;
+
+    PAR[PM_PAR_SKY]  = 0.0;
+    PAR[PM_PAR_I0]   = peak->flux;
+    PAR[PM_PAR_XPOS] = peak->xf;
+    PAR[PM_PAR_YPOS] = peak->yf;
+    PAR[PM_PAR_SXX]  = PS_MAX(0.5, M_SQRT2*shape.sx);
+    PAR[PM_PAR_SYY]  = PS_MAX(0.5, M_SQRT2*shape.sy);
+    PAR[PM_PAR_SXY]  = shape.sxy;
+    return(true);
+}
+
+psF64 PM_MODEL_FLUX (const psVector *params)
+{
+    float z, norm;
+    psEllipseShape shape;
+
+    psF32 *PAR = params->data.F32;
+
+    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
+    shape.sy  = PAR[PM_PAR_SYY] / M_SQRT2;
+    shape.sxy = PAR[PM_PAR_SXY];
+
+    // Area is equivalent to 2 pi sigma^2
+    psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
+    psF64 Area = 2.0 * M_PI * axes.major * axes.minor;
+
+    // the area needs to be multiplied by the integral of f(z) = exp(-sqrt(z)) [0 to infinity]
+    norm = 0.0;
+
+    # define DZ 0.25
+
+    float f0 = 1.0;
+    float f1, f2;
+    for (z = DZ; z < 50; z += DZ) {
+        f1 = exp(-sqrt(z));
+        z += DZ;
+        f2 = exp(-sqrt(z));
+        norm += f0 + 4*f1 + f2;
+        f0 = f2;
+    }
+    norm *= DZ / 3.0;
+
+    psF64 Flux = PAR[PM_PAR_I0] * Area * norm;
+
+    return(Flux);
+}
+
+// define this function so it never returns Inf or NaN
+// return the radius which yields the requested flux
+psF64 PM_MODEL_RADIUS (const psVector *params, psF64 flux)
+{
+    psEllipseShape shape;
+
+    psF32 *PAR = params->data.F32;
+
+    if (flux <= 0)
+        return (1.0);
+    if (PAR[PM_PAR_I0] <= 0)
+        return (1.0);
+    if (flux >= PAR[PM_PAR_I0])
+        return (1.0);
+
+    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
+    shape.sy  = PAR[PM_PAR_SYY] / M_SQRT2;
+    shape.sxy = PAR[PM_PAR_SXY];
+
+    psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
+
+    // f = Io exp(-sqrt(z)) -> sqrt(z) = ln(Io/f)
+    psF64 zn = log(PAR[PM_PAR_I0] / flux);
+    psF64 radius = axes.major * sqrt (2.0) * zn;
+
+    psAssert (isfinite(radius), "fix this code: z should not be nan for %f", PAR[PM_PAR_7]);
+    return (radius);
+}
+
+bool PM_MODEL_FROM_PSF (pmModel *modelPSF, pmModel *modelFLT, const pmPSF *psf)
+{
+
+    psF32 *out = modelPSF->params->data.F32;
+    psF32 *in  = modelFLT->params->data.F32;
+
+    // we require these two parameters to exist
+    assert (psf->params->n > PM_PAR_YPOS);
+    assert (psf->params->n > PM_PAR_XPOS);
+
+    for (int i = 0; i < psf->params->n; i++) {
+        if (psf->params->data[i] == NULL) {
+            out[i] = in[i];
+        } else {
+            pmTrend2D *trend = psf->params->data[i];
+            out[i] = pmTrend2DEval(trend, in[PM_PAR_XPOS], in[PM_PAR_YPOS]);
+        }
+    }
+
+    // the 2D PSF model fits polarization terms (E0,E1,E2)
+    // convert to shape terms (SXX,SYY,SXY)
+    if (!pmPSF_FitToModel (out, 0.1)) {
+        psTrace("psModules.objects", 5, "Failed to fit object at (r,c) = (%.1f,%.1f)", in[PM_PAR_YPOS], in[PM_PAR_XPOS]);
+        return false;
+    }
+
+    // apply the model limits here: this truncates excessive extrapolation
+    // XXX do we need to do this still?  should we put in asserts to test?
+    for (int i = 0; i < psf->params->n; i++) {
+        // apply the limits to all components or just the psf-model parameters?
+        if (psf->params->data[i] == NULL)
+            continue;
+
+        bool status = true;
+        status &= PM_MODEL_LIMITS(PS_MINIMIZE_PARAM_MIN, i, out, NULL);
+        status &= PM_MODEL_LIMITS(PS_MINIMIZE_PARAM_MAX, i, out, NULL);
+        if (!status) {
+            psTrace ("psModules.objects", 5, "Hitting parameter limits at (r,c) = (%.1f, %.1f)",
+                     in[PM_PAR_XPOS], in[PM_PAR_YPOS]);
+            modelPSF->flags |= PM_MODEL_STATUS_LIMITS;
+        }
+    }
+
+    return true;
+}
+
+// construct the PSF model from the FLT model and the psf
+// XXX is this sufficiently general do be a global function, not a pmModelClass function?
+bool PM_MODEL_PARAMS_FROM_PSF (pmModel *model, const pmPSF *psf, float Xo, float Yo, float Io)
+{
+    psF32 *PAR = model->params->data.F32;
+
+    // we require these two parameters to exist
+    assert (psf->params->n > PM_PAR_YPOS);
+    assert (psf->params->n > PM_PAR_XPOS);
+
+    PAR[PM_PAR_SKY]  = 0.0;
+    PAR[PM_PAR_I0]   = Io;
+    PAR[PM_PAR_XPOS] = Xo;
+    PAR[PM_PAR_YPOS] = Yo;
+
+    // supply the model-fitted parameters, or copy from the input
+    for (int i = 0; i < psf->params->n; i++) {
+        if (i == PM_PAR_SKY) continue;
+        if (i == PM_PAR_I0) continue;
+        if (i == PM_PAR_XPOS) continue;
+        if (i == PM_PAR_YPOS) continue;
+        pmTrend2D *trend = psf->params->data[i];
+        PAR[i] = pmTrend2DEval(trend, Xo, Yo);
+    }
+
+    // the 2D PSF model fits polarization terms (E0,E1,E2)
+    // convert to shape terms (SXX,SYY,SXY)
+    // XXX user-defined value for limit?
+    if (!pmPSF_FitToModel (PAR, 0.1)) {
+        psTrace ("psModules.objects", 3, "Failed to fit object at (r,c) = (%.1f,%.1f)", Xo, Yo);
+        return false;
+    }
+
+    // apply the model limits here: this truncates excessive extrapolation
+    // XXX do we need to do this still?  should we put in asserts to test?
+    for (int i = 0; i < psf->params->n; i++) {
+        // apply the limits to all components or just the psf-model parameters?
+        if (psf->params->data[i] == NULL)
+            continue;
+
+        bool status = true;
+        status &= PM_MODEL_LIMITS (PS_MINIMIZE_PARAM_MIN, i, PAR, NULL);
+        status &= PM_MODEL_LIMITS (PS_MINIMIZE_PARAM_MAX, i, PAR, NULL);
+        if (!status) {
+            psTrace ("psModules.objects", 5, "Hitting parameter limits at (r,c) = (%.1f, %.1f)", Xo, Yo);
+            model->flags |= PM_MODEL_STATUS_LIMITS;
+        }
+    }
+    return(true);
+}
+
+bool PM_MODEL_FIT_STATUS (pmModel *model)
+{
+    bool  status;
+
+    psF32 *PAR  = model->params->data.F32;
+    psF32 *dPAR = model->dparams->data.F32;
+
+    status = true;
+    status &= (PAR[PM_PAR_I0] > 0);
+    status &= ((dPAR[PM_PAR_I0]/PAR[PM_PAR_I0]) < 0.5);
+
+    return status;
+}
+
+
+void PM_MODEL_SET_LIMITS(pmModelLimitsType type)
+{
+    switch (type) {
+      case PM_MODEL_LIMITS_NONE:
+        paramsMinUse = NULL;
+        paramsMaxUse = NULL;
+        limitsApply = true;
+        break;
+      case PM_MODEL_LIMITS_IGNORE:
+        paramsMinUse = NULL;
+        paramsMaxUse = NULL;
+        limitsApply = false;
+        break;
+      case PM_MODEL_LIMITS_LAX:
+        paramsMinUse = paramsMinLax;
+        paramsMaxUse = paramsMaxLax;
+        limitsApply = true;
+        break;
+      case PM_MODEL_LIMITS_MODERATE:
+        paramsMinUse = paramsMinModerate;
+        paramsMaxUse = paramsMaxModerate;
+        limitsApply = true;
+        break;
+      case PM_MODEL_LIMITS_STRICT:
+        paramsMinUse = paramsMinStrict;
+        paramsMaxUse = paramsMaxStrict;
+        limitsApply = true;
+        break;
+      default:
+        psAbort("Unrecognised model limits type: %x", type);
+    }
+    return;
+}
Index: branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_EXP.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_EXP.h	(revision 29060)
+++ branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_EXP.h	(revision 29060)
@@ -0,0 +1,15 @@
+#ifndef PM_MODEL_EXP_H
+
+#include "pmModel.h"
+
+psF32 pmModelFunc_EXP(psVector *deriv, const psVector *params, const psVector *pixcoord);
+bool pmModelLimits_EXP(psMinConstraintMode mode, int nParam, float *params, float *beta);
+bool pmModelGuess_EXP(pmModel *model, pmSource *source);
+psF64 pmModelFlux_EXP(const psVector *params);
+psF64 pmModelRadius_EXP(const psVector *params, psF64 flux);
+bool pmModelFromPSF_EXP(pmModel *modelPSF, pmModel *modelFLT, const pmPSF *psf);
+bool  pmModelParamsFromPSF_EXP(pmModel *model, const pmPSF *psf, float Xo, float Yo, float Io);
+bool pmModelFitStatus_EXP(pmModel *model);
+void pmModelSetLimits_EXP(pmModelLimitsType type);
+
+#endif
Index: branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_GAUSS.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_GAUSS.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_GAUSS.c	(revision 29060)
@@ -21,11 +21,31 @@
 #include <stdio.h>
 #include <pslib.h>
-
+#include "pmHDU.h"
+#include "pmFPA.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmSpan.h"
+#include "pmFootprintSpans.h"
+#include "pmFootprint.h"
+#include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmPeaks.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
 #include "pmSource.h"
-#include "pmModel.h"
+#include "pmSourceFitModel.h"
+#include "pmPSF.h"
+#include "pmPSFtry.h"
+#include "pmDetections.h"
+
 #include "pmModel_GAUSS.h"
 
+# define PM_MODEL_NPARAM          7
 # define PM_MODEL_FUNC            pmModelFunc_GAUSS
 # define PM_MODEL_FLUX            pmModelFlux_GAUSS
@@ -83,4 +103,5 @@
         dPAR[PM_PAR_XPOS] = q*(2*px/PAR[PM_PAR_SXX] + Y*PAR[PM_PAR_SXY]);
         dPAR[PM_PAR_YPOS] = q*(2*py/PAR[PM_PAR_SYY] + X*PAR[PM_PAR_SXY]);
+
         // the extra factor of 2 below is needed to avoid excessive swings
         dPAR[PM_PAR_SXX]  = +4.0*q*px*px/PAR[PM_PAR_SXX];
@@ -102,5 +123,5 @@
         return true;
     }
-    psAssert(nParam >= 0 && nParam <= PM_PAR_7, "Parameter index is out of bounds");
+    psAssert(nParam >= 0 && nParam < PM_MODEL_NPARAM, "Parameter index is out of bounds");
 
     // we need to calculate the limits for SXY specially
@@ -347,4 +368,5 @@
 // this test is invalid if the parameters are derived
 // from the PSF model
+// XXX how is this used?  it prevents forced photometry from ever being 'successful'
 bool PM_MODEL_FIT_STATUS (pmModel *model)
 {
@@ -394,12 +416,2 @@
     return;
 }
-
-# undef PM_MODEL_FUNC
-# undef PM_MODEL_FLUX
-# undef PM_MODEL_GUESS
-# undef PM_MODEL_LIMITS
-# undef PM_MODEL_RADIUS
-# undef PM_MODEL_FROM_PSF
-# undef PM_MODEL_PARAMS_FROM_PSF
-# undef PM_MODEL_FIT_STATUS
-# undef PM_MODEL_SET_LIMITS
Index: branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_PGAUSS.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_PGAUSS.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_PGAUSS.c	(revision 29060)
@@ -21,11 +21,31 @@
 #include <stdio.h>
 #include <pslib.h>
-
+#include "pmHDU.h"
+#include "pmFPA.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmSpan.h"
+#include "pmFootprintSpans.h"
+#include "pmFootprint.h"
+#include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmPeaks.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
 #include "pmSource.h"
-#include "pmModel.h"
+#include "pmSourceFitModel.h"
+#include "pmPSF.h"
+#include "pmPSFtry.h"
+#include "pmDetections.h"
+
 #include "pmModel_PGAUSS.h"
 
+# define PM_MODEL_NPARAM          7
 # define PM_MODEL_FUNC            pmModelFunc_PGAUSS
 # define PM_MODEL_FLUX            pmModelFlux_PGAUSS
@@ -103,5 +123,5 @@
         return true;
     }
-    psAssert(nParam >= 0 && nParam <= PM_PAR_7, "Parameter index is out of bounds");
+    psAssert(nParam >= 0 && nParam < PM_MODEL_NPARAM, "Parameter index is out of bounds");
 
     // we need to calculate the limits for SXY specially
@@ -448,12 +468,2 @@
     return;
 }
-
-# undef PM_MODEL_FUNC
-# undef PM_MODEL_FLUX
-# undef PM_MODEL_GUESS
-# undef PM_MODEL_LIMITS
-# undef PM_MODEL_RADIUS
-# undef PM_MODEL_FROM_PSF
-# undef PM_MODEL_PARAMS_FROM_PSF
-# undef PM_MODEL_FIT_STATUS
-# undef PM_MODEL_SET_LIMITS
Index: branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_PS1_V1.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_PS1_V1.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_PS1_V1.c	(revision 29060)
@@ -22,11 +22,31 @@
 #include <stdio.h>
 #include <pslib.h>
-
+#include "pmHDU.h"
+#include "pmFPA.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmSpan.h"
+#include "pmFootprintSpans.h"
+#include "pmFootprint.h"
+#include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmPeaks.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
 #include "pmSource.h"
-#include "pmModel.h"
+#include "pmSourceFitModel.h"
+#include "pmPSF.h"
+#include "pmPSFtry.h"
+#include "pmDetections.h"
+
 #include "pmModel_PS1_V1.h"
 
+# define PM_MODEL_NPARAM          8
 # define PM_MODEL_FUNC            pmModelFunc_PS1_V1
 # define PM_MODEL_FLUX            pmModelFlux_PS1_V1
@@ -122,5 +142,5 @@
         return true;
     }
-    psAssert(nParam >= 0 && nParam <= PM_PAR_7, "Parameter index is out of bounds");
+    psAssert(nParam >= 0 && nParam < PM_MODEL_NPARAM, "Parameter index is out of bounds");
 
     // we need to calculate the limits for SXY specially
@@ -222,5 +242,5 @@
     PAR[PM_PAR_SYY]  = PS_MAX(0.5, M_SQRT2*shape.sy);
     PAR[PM_PAR_SXY]  = shape.sxy;
-    PAR[PM_PAR_7]    = 1.0;
+    PAR[PM_PAR_7]    = 0.5;
 
     return(true);
@@ -468,14 +488,2 @@
     return;
 }
-
-# undef PM_MODEL_FUNC
-# undef PM_MODEL_FLUX
-# undef PM_MODEL_GUESS
-# undef PM_MODEL_LIMITS
-# undef PM_MODEL_RADIUS
-# undef PM_MODEL_FROM_PSF
-# undef PM_MODEL_PARAMS_FROM_PSF
-# undef PM_MODEL_FIT_STATUS
-# undef PM_MODEL_SET_LIMITS
-# undef ALPHA
-# undef ALPHA_M
Index: branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_QGAUSS.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_QGAUSS.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_QGAUSS.c	(revision 29060)
@@ -22,11 +22,31 @@
 #include <stdio.h>
 #include <pslib.h>
-
+#include "pmHDU.h"
+#include "pmFPA.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmSpan.h"
+#include "pmFootprintSpans.h"
+#include "pmFootprint.h"
+#include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmPeaks.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
 #include "pmSource.h"
-#include "pmModel.h"
+#include "pmSourceFitModel.h"
+#include "pmPSF.h"
+#include "pmPSFtry.h"
+#include "pmDetections.h"
+
 #include "pmModel_QGAUSS.h"
 
+# define PM_MODEL_NPARAM          8
 # define PM_MODEL_FUNC            pmModelFunc_QGAUSS
 # define PM_MODEL_FLUX            pmModelFlux_QGAUSS
@@ -123,5 +143,5 @@
         return true;
     }
-    psAssert(nParam >= 0 && nParam <= PM_PAR_7, "Parameter index is out of bounds");
+    psAssert(nParam >= 0 && nParam < PM_MODEL_NPARAM, "Parameter index is out of bounds");
 
     // we need to calculate the limits for SXY specially
@@ -469,14 +489,2 @@
     return;
 }
-
-# undef PM_MODEL_FUNC
-# undef PM_MODEL_FLUX
-# undef PM_MODEL_GUESS
-# undef PM_MODEL_LIMITS
-# undef PM_MODEL_RADIUS
-# undef PM_MODEL_FROM_PSF
-# undef PM_MODEL_PARAMS_FROM_PSF
-# undef PM_MODEL_FIT_STATUS
-# undef PM_MODEL_SET_LIMITS
-# undef ALPHA
-# undef ALPHA_M
Index: branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_RGAUSS.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_RGAUSS.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_RGAUSS.c	(revision 29060)
@@ -22,11 +22,31 @@
 #include <stdio.h>
 #include <pslib.h>
-
+#include "pmHDU.h"
+#include "pmFPA.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmSpan.h"
+#include "pmFootprintSpans.h"
+#include "pmFootprint.h"
+#include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmPeaks.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
 #include "pmSource.h"
-#include "pmModel.h"
+#include "pmSourceFitModel.h"
+#include "pmPSF.h"
+#include "pmPSFtry.h"
+#include "pmDetections.h"
+
 #include "pmModel_RGAUSS.h"
 
+# define PM_MODEL_NPARAM          8
 # define PM_MODEL_FUNC            pmModelFunc_RGAUSS
 # define PM_MODEL_FLUX            pmModelFlux_RGAUSS
@@ -112,5 +132,5 @@
         return true;
     }
-    psAssert(nParam >= 0 && nParam <= PM_PAR_7, "Parameter index is out of bounds");
+    psAssert(nParam >= 0 && nParam < PM_MODEL_NPARAM, "Parameter index is out of bounds");
 
     // we need to calculate the limits for SXY specially
@@ -209,8 +229,8 @@
     PAR[PM_PAR_XPOS] = peak->xf;
     PAR[PM_PAR_YPOS] = peak->yf;
-    PAR[PM_PAR_SXX]  = PS_MAX(0.5, M_SQRT2*shape.sx);
-    PAR[PM_PAR_SYY]  = PS_MAX(0.5, M_SQRT2*shape.sy);
+    PAR[PM_PAR_SXX]  = PS_MAX(0.5, shape.sx);
+    PAR[PM_PAR_SYY]  = PS_MAX(0.5, shape.sy);
     PAR[PM_PAR_SXY]  = shape.sxy;
-    PAR[PM_PAR_7]    = 2.25;
+    PAR[PM_PAR_7]    = 1.5;
 
     return(true);
@@ -463,12 +483,2 @@
     return;
 }
-
-# undef PM_MODEL_FUNC
-# undef PM_MODEL_FLUX
-# undef PM_MODEL_GUESS
-# undef PM_MODEL_LIMITS
-# undef PM_MODEL_RADIUS
-# undef PM_MODEL_FROM_PSF
-# undef PM_MODEL_PARAMS_FROM_PSF
-# undef PM_MODEL_FIT_STATUS
-# undef PM_MODEL_SET_LIMITS
Index: branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_SERSIC.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_SERSIC.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/models/pmModel_SERSIC.c	(revision 29060)
@@ -18,5 +18,5 @@
    * PM_PAR_7   7   - normalized sersic parameter
 
-   note that a standard sersic model uses exp(-K*(z^(1/n) - 1).  the additional elements (K,
+   note that a standard sersic model uses exp(-K*(z^(1/2n) - 1).  the additional elements (K,
    the -1 offset) are absorbed in this model by the normalization, the exponent, and the
    radial scale.  We fit the elements in this form, then re-normalize them on output.
@@ -25,11 +25,31 @@
 #include <stdio.h>
 #include <pslib.h>
-
+#include "pmHDU.h"
+#include "pmFPA.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmSpan.h"
+#include "pmFootprintSpans.h"
+#include "pmFootprint.h"
+#include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmPeaks.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
 #include "pmSource.h"
-#include "pmModel.h"
+#include "pmSourceFitModel.h"
+#include "pmPSF.h"
+#include "pmPSFtry.h"
+#include "pmDetections.h"
+
 #include "pmModel_SERSIC.h"
 
+# define PM_MODEL_NPARAM          8
 # define PM_MODEL_FUNC            pmModelFunc_SERSIC
 # define PM_MODEL_FLUX            pmModelFlux_SERSIC
@@ -47,5 +67,5 @@
 
 // Lax parameter limits
-static float paramsMinLax[] = { -1.0e3, 1.0e-2, -100, -100, 0.05, 0.05, -1.0, 0.05 };
+static float paramsMinLax[] = { -1.0e3, 1.0e-2, -100, -100, 0.001, 0.001, -1.0, 0.05 };
 static float paramsMaxLax[] = { 1.0e5, 1.0e8, 1.0e4, 1.0e4, 100, 100, 1.0, 4.0 };
 
@@ -84,6 +104,10 @@
     assert (z >= 0);
 
-    psF32 f2 = pow(z,PAR[PM_PAR_7]);
-    psF32 f1 = exp(-f2);
+    float index = 0.5 / PAR[PM_PAR_7];
+    float bn = 1.9992*index - 0.3271;
+    float Io = exp(bn);
+
+    psF32 f2 = bn*pow(z,PAR[PM_PAR_7]);
+    psF32 f1 = Io*exp(-f2);
     psF32 z0 = PAR[PM_PAR_I0]*f1;
     psF32 f0 = PAR[PM_PAR_SKY] + z0;
@@ -98,9 +122,10 @@
 
         // gradient is infinite for z = 0; saturate at z = 0.01
-        psF32 z1 = (z < 0.01) ? z0*PAR[PM_PAR_7]*pow(0.01,PAR[PM_PAR_7] - 1.0) : z0*PAR[PM_PAR_7]*pow(z,PAR[PM_PAR_7] - 1.0);
+        psF32 z1 = (z < 0.01) ? z0*bn*PAR[PM_PAR_7]*pow(0.01,PAR[PM_PAR_7] - 1.0) : z0*bn*PAR[PM_PAR_7]*pow(z,PAR[PM_PAR_7] - 1.0);
 
         dPAR[PM_PAR_SKY]  = +1.0;
         dPAR[PM_PAR_I0]   = +f1;
-        dPAR[PM_PAR_7]    = (z == 0.0) ? 0.0 : -z0*f2*log(z);
+        dPAR[PM_PAR_7]    = (z < 0.01) ? -z0*pow(0.01,PAR[PM_PAR_7])*log(0.01) : -z0*f2*log(z);
+	dPAR[PM_PAR_7]   *= 3.0;
 
         assert (isfinite(z1));
@@ -109,7 +134,6 @@
         dPAR[PM_PAR_XPOS] = +1.0*z1*(2.0*px/PAR[PM_PAR_SXX] + Y*PAR[PM_PAR_SXY]);
         dPAR[PM_PAR_YPOS] = +1.0*z1*(2.0*py/PAR[PM_PAR_SYY] + X*PAR[PM_PAR_SXY]);
-        dPAR[PM_PAR_SXX]  = +2.0*z1*px*px/PAR[PM_PAR_SXX];
+        dPAR[PM_PAR_SXX]  = +2.0*z1*px*px/PAR[PM_PAR_SXX]; // XXX : increase drag?
         dPAR[PM_PAR_SYY]  = +2.0*z1*py*py/PAR[PM_PAR_SYY];
-        dPAR[PM_PAR_SXY]  = -1.0*z1*X*Y;
         dPAR[PM_PAR_SXY]  = -1.0*z1*X*Y;
     }
@@ -127,5 +151,5 @@
         return true;
     }
-    psAssert(nParam >= 0 && nParam <= PM_PAR_7, "Parameter index is out of bounds");
+    psAssert(nParam >= 0 && nParam < PM_MODEL_NPARAM, "Parameter index is out of bounds");
 
     // we need to calculate the limits for SXY specially
@@ -201,4 +225,15 @@
     psF32     *PAR  = model->params->data.F32;
 
+    // the other parameters depend on the guess for PAR_7
+    if (!isfinite(PAR[PM_PAR_7])) {
+	PAR[PM_PAR_7]    = 0.25;
+    }    
+    float index = 0.5 / PAR[PM_PAR_7];
+
+    // the scale-length is a function of the moments and the index:
+    // Rmajor_guess = Rmajor_moments * Scale * Zero
+    float Scale = 0.70 + 0.053 * PAR[PM_PAR_7];
+    float Zero  = 1.16 - 0.615 * PAR[PM_PAR_7];
+
     psEllipseMoments emoments;
     emoments.x2 = moments->Mxx;
@@ -213,4 +248,12 @@
     if (!isfinite(axes.theta)) return false;
 
+    // set a lower limit to avoid absurd solutions..
+    float Rmajor = PS_MAX(1.0, Scale * axes.major + Zero);
+    float Rminor = Rmajor * (axes.minor / axes.major);
+
+    // fprintf (stderr, "guess index: %f : %f, %f -> %f, %f\n", index, axes.major, axes.minor, Rmajor, Rminor);
+
+    axes.major = Rmajor;
+    axes.minor = Rminor;
     psEllipseShape shape = psEllipseAxesToShape (axes);
 
@@ -219,12 +262,22 @@
     if (!isfinite(shape.sxy)) return false;
 
+    float bn = 1.9992*index - 0.3271;
+    // float fR = 1.0 / (sqrt(2.0) * pow (bn, index));
+    float Io = exp(0.5*bn);
+
+    // XXX do we need this factor of sqrt(2)?
+    // float Sxx = PS_MAX(0.5, M_SQRT2*shape.sx);
+    // float Syy = PS_MAX(0.5, M_SQRT2*shape.sy);
+
+    float Sxx = PS_MAX(0.5, shape.sx);
+    float Syy = PS_MAX(0.5, shape.sy);
+
     PAR[PM_PAR_SKY]  = 0.0;
-    PAR[PM_PAR_I0]   = peak->flux;
+    PAR[PM_PAR_I0]   = peak->flux / Io;
     PAR[PM_PAR_XPOS] = peak->xf;
     PAR[PM_PAR_YPOS] = peak->yf;
-    PAR[PM_PAR_SXX]  = PS_MAX(0.5, M_SQRT2*shape.sx);
-    PAR[PM_PAR_SYY]  = PS_MAX(0.5, M_SQRT2*shape.sy);
+    PAR[PM_PAR_SXX]  = Sxx;
+    PAR[PM_PAR_SYY]  = Syy;
     PAR[PM_PAR_SXY]  = shape.sxy;
-    PAR[PM_PAR_7]    = 0.5;
 
     return(true);
@@ -254,7 +307,8 @@
     float f1, f2;
     for (z = DZ; z < 50; z += DZ) {
-        f1 = 1.0 / (1 + PAR[PM_PAR_7]*z + pow(z, 2.25));
+        // f1 = 1.0 / (1 + PAR[PM_PAR_7]*z + pow(z, 2.25));
+	f1 = exp(-pow(z,PAR[PM_PAR_7]));
         z += DZ;
-        f2 = 1.0 / (1 + PAR[PM_PAR_7]*z + pow(z, 2.25));
+	f2 = exp(-pow(z,PAR[PM_PAR_7]));
         norm += f0 + 4*f1 + f2;
         f0 = f2;
@@ -287,17 +341,12 @@
 
     psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
-    psF64 sigma = axes.major;
-
-    psF64 limit = flux / PAR[PM_PAR_I0];
-
-    psF64 z = pow (-log(limit), (1.0 / PAR[PM_PAR_7]));
-    psAssert (isfinite(z), "fix this code: z should not be nan for %f", PAR[PM_PAR_7]);
-
-    psF64 radius = sigma * sqrt (2.0 * z);
-    psAssert (isfinite(radius), "fix this code: radius should not be nan for %f, %f", PAR[PM_PAR_7], sigma);
-
-    if (isnan(radius))
-        psAbort("error in code: radius is NaN");
-
+
+    // f = Io exp(-z^n) -> z^n = ln(Io/f)
+    psF64 zn = log(PAR[PM_PAR_I0] / flux);
+    psF64 radius = axes.major * sqrt (2.0) * pow(zn, 0.5 / PAR[PM_PAR_7]);
+
+    // fprintf (stderr, "sersic model %f %f, n %f, radius: %f, zn: %f, f/Io: %f, major: %f\n", PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], PAR[PM_PAR_7], radius, zn, flux/PAR[PM_PAR_I0], axes.major);
+
+    psAssert (isfinite(radius), "fix this code: z should not be nan for %f", PAR[PM_PAR_7]);
     return (radius);
 }
@@ -448,12 +497,2 @@
     return;
 }
-
-# undef PM_MODEL_FUNC
-# undef PM_MODEL_FLUX
-# undef PM_MODEL_GUESS
-# undef PM_MODEL_LIMITS
-# undef PM_MODEL_RADIUS
-# undef PM_MODEL_FROM_PSF
-# undef PM_MODEL_PARAMS_FROM_PSF
-# undef PM_MODEL_FIT_STATUS
-# undef PM_MODEL_SET_LIMITS
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmDetEff.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmDetEff.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmDetEff.c	(revision 29060)
@@ -3,8 +3,30 @@
 #endif
 
+#include <string.h>
 #include <pslib.h>
+#include "pmHDU.h"
+#include "pmFPA.h"
 
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmSpan.h"
+#include "pmFootprintSpans.h"
+#include "pmFootprint.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
+#include "pmPSF.h"
+#include "pmPSFtry.h"
+#include "pmDetections.h"
 #include "pmDetEff.h"
-
 
 static void detEffFree(pmDetEff *de)
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmDetEff.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmDetEff.h	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmDetEff.h	(revision 29060)
@@ -1,9 +1,4 @@
 #ifndef PM_DET_EFF_H
 #define PM_DET_EFF_H
-
-#include <pslib.h>
-#include <string.h>
-
-#include "pmFPA.h"
 
 #define PM_DETEFF_ANALYSIS "DETEFF"     // Location of detection efficiency on pmReadout.analysis
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmFootprint.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmFootprint.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmFootprint.c	(revision 29060)
@@ -19,4 +19,5 @@
 #include <pslib.h>
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmFootprint.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmFootprint.h	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmFootprint.h	(revision 29060)
@@ -10,8 +10,4 @@
 #ifndef PM_FOOTPRINT_H
 #define PM_FOOTPRINT_H
-
-#include <pslib.h>
-#include "pmSpan.h"
-#include "pmFootprintSpans.h"
 
 typedef struct {
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmFootprintArrayGrow.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmFootprintArrayGrow.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmFootprintArrayGrow.c	(revision 29060)
@@ -18,4 +18,5 @@
 #include <pslib.h>
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmFootprintArraysMerge.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmFootprintArraysMerge.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmFootprintArraysMerge.c	(revision 29060)
@@ -18,4 +18,5 @@
 #include <pslib.h>
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmFootprintAssignPeaks.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmFootprintAssignPeaks.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmFootprintAssignPeaks.c	(revision 29060)
@@ -18,4 +18,5 @@
 #include <pslib.h>
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmFootprintCullPeaks.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmFootprintCullPeaks.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmFootprintCullPeaks.c	(revision 29060)
@@ -19,6 +19,6 @@
 #include <pslib.h>
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
-#include "pmFootprintSpans.h"
 #include "pmPeaks.h"
 
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmFootprintFind.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmFootprintFind.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmFootprintFind.c	(revision 29060)
@@ -19,4 +19,5 @@
 #include <pslib.h>
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmFootprintFindAtPoint.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmFootprintFindAtPoint.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmFootprintFindAtPoint.c	(revision 29060)
@@ -19,7 +19,7 @@
 #include <pslib.h>
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
-#include "pmFootprintSpans.h"
 
 /************************************************************************************************************/
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmFootprintIDs.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmFootprintIDs.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmFootprintIDs.c	(revision 29060)
@@ -19,4 +19,5 @@
 #include <pslib.h>
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmFootprintSpans.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmFootprintSpans.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmFootprintSpans.c	(revision 29060)
@@ -19,7 +19,7 @@
 #include <pslib.h>
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
-#include "pmFootprintSpans.h"
 
 static void pmStartSpanFree(pmStartSpan *sspan) {
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmFootprintSpans.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmFootprintSpans.h	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmFootprintSpans.h	(revision 29060)
@@ -10,7 +10,4 @@
 #ifndef PM_FOOTPRINT_SPANS_H
 #define PM_FOOTPRINT_SPANS_H
-
-#include <pslib.h>
-#include "pmSpan.h"
 
 /* We define two helper structures used in building the pmFootprints:
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmGrowthCurve.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmGrowthCurve.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmGrowthCurve.c	(revision 29060)
@@ -20,14 +20,26 @@
 #include "pmHDU.h"
 #include "pmFPA.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmResiduals.h"
-#include "pmGrowthCurve.h"
-#include "pmTrend2D.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
-#include "pmSource.h"
+#include "pmPSFtry.h"
+#include "pmDetections.h"
+
 #include "psVectorBracket.h"
 
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmGrowthCurveGenerate.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmGrowthCurveGenerate.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmGrowthCurveGenerate.c	(revision 29060)
@@ -24,20 +24,30 @@
 #include "pmHDU.h"
 #include "pmFPA.h"
+#include "pmFPAMaskWeight.h"
+#include "psVectorBracket.h"
+#include "pmErrorCodes.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmResiduals.h"
-#include "pmGrowthCurve.h"
-#include "pmTrend2D.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
-#include "pmSource.h"
-#include "pmModelClass.h"
-#include "pmModelUtils.h"
+#include "pmPSFtry.h"
+#include "pmDetections.h"
+
 #include "pmSourcePhotometry.h"
-#include "pmFPAMaskWeight.h"
-#include "psVectorBracket.h"
-#include "pmErrorCodes.h"
 
 pmGrowthCurve *pmGrowthCurveForPosition (psImage *image, pmPSF *psf, bool ignore, psImageMaskType maskVal, psImageMaskType markVal, float xc, float yc);
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmModel.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmModel.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmModel.c	(revision 29060)
@@ -23,8 +23,14 @@
 #include "pmHDU.h"
 #include "pmFPA.h"
+
+#include "pmTrend2D.h"
 #include "pmResiduals.h"
 #include "pmGrowthCurve.h"
-#include "pmTrend2D.h"
-#include "pmPSF.h"
+#include "pmSpan.h"
+#include "pmFootprintSpans.h"
+#include "pmFootprint.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmModelFuncs.h"
 #include "pmModel.h"
 #include "pmModelClass.h"
@@ -75,6 +81,6 @@
 
     for (psS32 i = 0; i < tmp->params->n; i++) {
-        tmp->params->data.F32[i] = 0.0;
-        tmp->dparams->data.F32[i] = 0.0;
+        tmp->params->data.F32[i] = NAN;
+        tmp->dparams->data.F32[i] = NAN;
     }
 
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmModel.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmModel.h	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmModel.h	(revision 29060)
@@ -14,7 +14,4 @@
 # define PM_MODEL_H
 
-#include <pslib.h>
-#include "pmPSF.h"
-
 /// @addtogroup Objects Object Detection / Analysis Functions
 /// @{
@@ -22,68 +19,4 @@
 /* pointers for the functions types below are supplied to each pmModel, and can be used by
    the programmer without needing to know the model class */
-
-typedef enum {
-    PM_MODEL_STATUS_NONE         = 0x00, ///< model fit not yet attempted, no other info
-    PM_MODEL_STATUS_FITTED       = 0x01, ///< model fit completed
-    PM_MODEL_STATUS_NONCONVERGE  = 0x02, ///< model fit did not converge
-    PM_MODEL_STATUS_OFFIMAGE     = 0x04, ///< model fit drove out of range
-    PM_MODEL_STATUS_BADARGS      = 0x08, ///< model fit called with invalid args
-    PM_MODEL_STATUS_LIMITS       = 0x10  ///< model parameters hit limits
-} pmModelStatus;
-
-typedef enum {
-    PM_MODEL_OP_NONE    = 0x00,
-    PM_MODEL_OP_FUNC    = 0x01,
-    PM_MODEL_OP_RES0    = 0x02,
-    PM_MODEL_OP_RES1    = 0x04,
-    PM_MODEL_OP_FULL    = 0x07,
-    PM_MODEL_OP_SKY     = 0x08,
-    PM_MODEL_OP_CENTER  = 0x10,
-    PM_MODEL_OP_NORM    = 0x20,
-    PM_MODEL_OP_NOISE   = 0x40,
-} pmModelOpMode;
-
-/// Parameter limit types
-typedef enum {
-    PM_MODEL_LIMITS_NONE,               ///< Apply no limits: suitable for debugging
-    PM_MODEL_LIMITS_IGNORE,             ///< Ignore all limits: fit can go to town
-    PM_MODEL_LIMITS_LAX,                ///< Lax limits: attempting to reproduce even bad data
-    PM_MODEL_LIMITS_MODERATE,           ///< Moderate limits: cope with mildly bad data
-    PM_MODEL_LIMITS_STRICT,             ///< Strict limits: insist on good quality data
-} pmModelLimitsType;
-
-typedef struct pmModel pmModel;
-typedef struct pmSource pmSource;
-
-//  This function is the model chi-square minimization function for this model.
-typedef psMinimizeLMChi2Func pmModelFunc;
-
-//  This function sets the parameter limits for this model.
-typedef psMinimizeLMLimitFunc pmModelLimits;
-
-// This function returns the integrated flux for the given model parameters.
-typedef psF64 (*pmModelFlux)(const psVector *params);
-
-// This function returns the radius at which the given model and parameters
-// achieves the given flux.
-typedef psF64 (*pmModelRadius)(const psVector *params, double flux);
-
-//  This function provides the model guess parameters based on the details of
-//  the given source.
-typedef bool (*pmModelGuessFunc)(pmModel *model, pmSource *source);
-
-//  This function constructs the PSF model for the given source based on the
-//  supplied psf and the EXT model for the object.
-typedef bool (*pmModelFromPSFFunc)(pmModel *modelPSF, pmModel *modelEXT, const pmPSF *psf);
-
-//  This function sets the model parameters based on the PSF for a given coordinate and central
-//  intensity
-typedef bool (*pmModelParamsFromPSF)(pmModel *model, const pmPSF *psf, float Xo, float Yo, float Io);
-
-//  This function returns the success / failure status of the given model fit
-typedef bool (*pmModelFitStatusFunc)(pmModel *model);
-
-//  This function sets the parameter limits for the given model
-typedef bool (*pmModelSetLimitsFunc)(pmModelLimitsType type);
 
 /** pmModel data structure
@@ -122,18 +55,4 @@
     pmModelSetLimitsFunc modelSetLimits;
 };
-
-/** Symbolic names for the elements of [d]params
- * Note: these are #defines not enums as a given element of [d]params
- * may/will correspond to different parameters in different contexts
- */
-#define PM_PAR_SKY 0   ///< Sky
-#define PM_PAR_I0 1   ///< Central intensity
-#define PM_PAR_XPOS 2   ///< X centre of object
-#define PM_PAR_YPOS 3   ///< Y centre of object
-#define PM_PAR_SXX 4   ///< shape X^2 moment
-#define PM_PAR_SYY 5   ///< shape Y^2 moment
-#define PM_PAR_SXY 6   ///< shape XY moment
-#define PM_PAR_7 7   ///< ??? Unknown parameter
-#define PM_PAR_8 8   ///< ??? Unknown parameter
 
 /** pmModelAlloc()
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmModelClass.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmModelClass.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmModelClass.c	(revision 29060)
@@ -23,15 +23,18 @@
 #include "pmHDU.h"
 #include "pmFPA.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmResiduals.h"
-#include "pmGrowthCurve.h"
-#include "pmTrend2D.h"
-#include "pmPSF.h"
+#include "pmModelFuncs.h"
 #include "pmModel.h"
-#include "pmSource.h"
+#include "pmModelUtils.h"
 #include "pmModelClass.h"
+
 #include "pmErrorCodes.h"
 
@@ -46,4 +49,6 @@
 # include "models/pmModel_RGAUSS.h"
 # include "models/pmModel_SERSIC.h"
+# include "models/pmModel_EXP.h"
+# include "models/pmModel_DEV.h"
 
 static pmModelClass defaultModels[] = {
@@ -53,5 +58,7 @@
     {"PS_MODEL_PS1_V1",       8, (pmModelFunc)pmModelFunc_PS1_V1,  (pmModelFlux)pmModelFlux_PS1_V1,  (pmModelRadius)pmModelRadius_PS1_V1,  (pmModelLimits)pmModelLimits_PS1_V1,  (pmModelGuessFunc)pmModelGuess_PS1_V1, (pmModelFromPSFFunc)pmModelFromPSF_PS1_V1, (pmModelParamsFromPSF)pmModelParamsFromPSF_PS1_V1, (pmModelFitStatusFunc)pmModelFitStatus_PS1_V1, (pmModelSetLimitsFunc)pmModelSetLimits_PS1_V1 },
     {"PS_MODEL_RGAUSS",       8, (pmModelFunc)pmModelFunc_RGAUSS,  (pmModelFlux)pmModelFlux_RGAUSS,  (pmModelRadius)pmModelRadius_RGAUSS,  (pmModelLimits)pmModelLimits_RGAUSS,  (pmModelGuessFunc)pmModelGuess_RGAUSS, (pmModelFromPSFFunc)pmModelFromPSF_RGAUSS, (pmModelParamsFromPSF)pmModelParamsFromPSF_RGAUSS, (pmModelFitStatusFunc)pmModelFitStatus_RGAUSS, (pmModelSetLimitsFunc)pmModelSetLimits_RGAUSS },
-    {"PS_MODEL_SERSIC",       8, (pmModelFunc)pmModelFunc_SERSIC,  (pmModelFlux)pmModelFlux_SERSIC,  (pmModelRadius)pmModelRadius_SERSIC,  (pmModelLimits)pmModelLimits_SERSIC,  (pmModelGuessFunc)pmModelGuess_SERSIC, (pmModelFromPSFFunc)pmModelFromPSF_SERSIC, (pmModelParamsFromPSF)pmModelParamsFromPSF_SERSIC, (pmModelFitStatusFunc)pmModelFitStatus_SERSIC, (pmModelSetLimitsFunc)pmModelSetLimits_SERSIC }
+    {"PS_MODEL_SERSIC",       8, (pmModelFunc)pmModelFunc_SERSIC,  (pmModelFlux)pmModelFlux_SERSIC,  (pmModelRadius)pmModelRadius_SERSIC,  (pmModelLimits)pmModelLimits_SERSIC,  (pmModelGuessFunc)pmModelGuess_SERSIC, (pmModelFromPSFFunc)pmModelFromPSF_SERSIC, (pmModelParamsFromPSF)pmModelParamsFromPSF_SERSIC, (pmModelFitStatusFunc)pmModelFitStatus_SERSIC, (pmModelSetLimitsFunc)pmModelSetLimits_SERSIC },
+    {"PS_MODEL_EXP",          7, (pmModelFunc)pmModelFunc_EXP,     (pmModelFlux)pmModelFlux_EXP,     (pmModelRadius)pmModelRadius_EXP,     (pmModelLimits)pmModelLimits_EXP,     (pmModelGuessFunc)pmModelGuess_EXP,    (pmModelFromPSFFunc)pmModelFromPSF_EXP,    (pmModelParamsFromPSF)pmModelParamsFromPSF_EXP,    (pmModelFitStatusFunc)pmModelFitStatus_EXP,    (pmModelSetLimitsFunc)pmModelSetLimits_EXP },
+    {"PS_MODEL_DEV",          7, (pmModelFunc)pmModelFunc_DEV,     (pmModelFlux)pmModelFlux_DEV,     (pmModelRadius)pmModelRadius_DEV,     (pmModelLimits)pmModelLimits_DEV,     (pmModelGuessFunc)pmModelGuess_DEV,    (pmModelFromPSFFunc)pmModelFromPSF_DEV,    (pmModelParamsFromPSF)pmModelParamsFromPSF_DEV,    (pmModelFitStatusFunc)pmModelFitStatus_DEV,    (pmModelSetLimitsFunc)pmModelSetLimits_DEV },
 };
 
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmModelClass.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmModelClass.h	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmModelClass.h	(revision 29060)
@@ -28,6 +28,4 @@
 # ifndef PM_MODEL_CLASS_H
 # define PM_MODEL_CLASS_H
-
-#include <pmModel.h>
 
 /// @addtogroup Objects Object Detection / Analysis Functions
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmModelFuncs.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmModelFuncs.h	(revision 29060)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmModelFuncs.h	(revision 29060)
@@ -0,0 +1,120 @@
+/* @file  pmModelClass.h
+ *
+ * The object model function types are desined to allow for the flexible addition of new
+ * object models. Every object model, with parameters represented by pmModel, has an
+ * associated set of functions which provide necessary support operations.  A These
+ * functions allow the programmer to select the approriate function or property for a
+ * specific object model class.
+ *
+ * Every model instance belongs to a class of models, defined by the value of the
+ * pmModelType type entry. Various functions need access to information about each of the
+ * models. Some of this information varies from model to model, and may depend on the
+ * current parameter values or other data quantities. In order to keep the code from
+ * requiring the information about each model to be coded into the low-level fitting
+ * routines, we define a collection of functions which allow us to abstract this type of
+ * model-dependent information. These generic functions take the model type and return the
+ * corresponding function pointer for the specified model. Each model is defined by
+ * creating this collection of specific functions, and placing them in a single file for
+ * each model. We define the following structure to carry the collection of information
+ * about the models.
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-11-27 03:14:57 $
+ * Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+# ifndef PM_MODEL_FUNCS_H
+# define PM_MODEL_FUNCS_H
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+// type of model carried by the pmModel structure
+typedef int pmModelType;
+
+typedef enum {
+    PM_MODEL_STATUS_NONE         = 0x00, ///< model fit not yet attempted, no other info
+    PM_MODEL_STATUS_FITTED       = 0x01, ///< model fit completed
+    PM_MODEL_STATUS_NONCONVERGE  = 0x02, ///< model fit did not converge
+    PM_MODEL_STATUS_OFFIMAGE     = 0x04, ///< model fit drove out of range
+    PM_MODEL_STATUS_BADARGS      = 0x08, ///< model fit called with invalid args
+    PM_MODEL_STATUS_LIMITS       = 0x10, ///< model parameters hit limits
+    PM_MODEL_STATUS_WEAK_FIT     = 0x20, ///< model fit met loose tolerance, but not tight tolerance
+} pmModelStatus;
+
+typedef enum {
+    PM_MODEL_OP_NONE    = 0x00,
+    PM_MODEL_OP_FUNC    = 0x01,
+    PM_MODEL_OP_RES0    = 0x02,
+    PM_MODEL_OP_RES1    = 0x04,
+    PM_MODEL_OP_FULL    = 0x07,
+    PM_MODEL_OP_SKY     = 0x08,
+    PM_MODEL_OP_CENTER  = 0x10,
+    PM_MODEL_OP_NORM    = 0x20,
+    PM_MODEL_OP_NOISE   = 0x40,
+} pmModelOpMode;
+
+/// Parameter limit types
+typedef enum {
+    PM_MODEL_LIMITS_NONE,               ///< Apply no limits: suitable for debugging
+    PM_MODEL_LIMITS_IGNORE,             ///< Ignore all limits: fit can go to town
+    PM_MODEL_LIMITS_LAX,                ///< Lax limits: attempting to reproduce even bad data
+    PM_MODEL_LIMITS_MODERATE,           ///< Moderate limits: cope with mildly bad data
+    PM_MODEL_LIMITS_STRICT,             ///< Strict limits: insist on good quality data
+} pmModelLimitsType;
+
+/** Symbolic names for the elements of [d]params
+ * Note: these are #defines not enums as a given element of [d]params
+ * may/will correspond to different parameters in different contexts
+ */
+#define PM_PAR_SKY  0   ///< Sky
+#define PM_PAR_I0   1   ///< Central intensity
+#define PM_PAR_XPOS 2   ///< X center of object
+#define PM_PAR_YPOS 3   ///< Y center of object
+#define PM_PAR_SXX  4   ///< shape X^2 moment
+#define PM_PAR_SYY  5   ///< shape Y^2 moment
+#define PM_PAR_SXY  6   ///< shape XY moment
+#define PM_PAR_7    7   ///< Model-dependent parameter
+#define PM_PAR_8    8   ///< Model-dependent parameter
+
+/*** these prototype classes are used to define elements of the pmModelClass structure below ***/
+ 
+typedef struct pmModel  pmModel;
+typedef struct pmSource pmSource;
+typedef struct pmPSF    pmPSF;
+
+//  This function is the model chi-square minimization function for this model.
+typedef psMinimizeLMChi2Func pmModelFunc;
+
+//  This function sets the parameter limits for this model.
+typedef psMinimizeLMLimitFunc pmModelLimits;
+
+// This function returns the integrated flux for the given model parameters.
+typedef psF64 (*pmModelFlux)(const psVector *params);
+
+// This function returns the radius at which the given model and parameters
+// achieves the given flux.
+typedef psF64 (*pmModelRadius)(const psVector *params, double flux);
+
+//  This function provides the model guess parameters based on the details of
+//  the given source.
+typedef bool (*pmModelGuessFunc)(pmModel *model, pmSource *source);
+
+//  This function constructs the PSF model for the given source based on the
+//  supplied psf and the EXT model for the object.
+typedef bool (*pmModelFromPSFFunc)(pmModel *modelPSF, pmModel *modelEXT, const pmPSF *psf);
+
+//  This function sets the model parameters based on the PSF for a given coordinate and central
+//  intensity
+typedef bool (*pmModelParamsFromPSF)(pmModel *model, const pmPSF *psf, float Xo, float Yo, float Io);
+
+//  This function returns the success / failure status of the given model fit
+typedef bool (*pmModelFitStatusFunc)(pmModel *model);
+
+//  This function sets the parameter limits for the given model
+typedef bool (*pmModelSetLimitsFunc)(pmModelLimitsType type);
+
+/// @}
+# endif /* PM_MODEL_FUNCS_H */
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmModelUtils.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmModelUtils.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmModelUtils.c	(revision 29060)
@@ -22,11 +22,25 @@
 #include "pmHDU.h"
 #include "pmFPA.h"
+
+#include "pmTrend2D.h"
 #include "pmResiduals.h"
 #include "pmGrowthCurve.h"
-#include "pmTrend2D.h"
+#include "pmSpan.h"
+#include "pmFootprintSpans.h"
+#include "pmFootprint.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
+
 #include "pmErrorCodes.h"
-#include "pmModelUtils.h"
 
 /*****************************************************************************
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmMoments.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmMoments.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmMoments.c	(revision 29060)
@@ -29,4 +29,13 @@
     psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
     pmMoments *tmp = (pmMoments *) psAlloc(sizeof(pmMoments));
+
+    tmp->Mrf = 0.0;
+    tmp->Mrh = 0.0;
+    tmp->KronFlux = 0.0;
+    tmp->KronFluxErr = 0.0;
+
+    tmp->KronFinner = 0.0;
+    tmp->KronFouter = 0.0;
+
     tmp->Mx = 0.0;
     tmp->My = 0.0;
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmMoments.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmMoments.h	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmMoments.h	(revision 29060)
@@ -24,4 +24,7 @@
 typedef struct
 {
+    float Mrf;    ///< radial first moment
+    float Mrh;    ///< radial half moment
+
     float Mx;     ///< X-coord of centroid.
     float My;     ///< Y-coord of centroid.
@@ -47,4 +50,11 @@
     float SN;     ///< approx signal-to-noise
     int nPixels;  ///< Number of pixels used.
+
+    float KronFlux;    ///< Kron flux (flux in 2.5*Mrf)
+    float KronFluxErr; ///< Kron flux error
+
+    float KronFinner;    ///< Kron flux (flux in 2.5*Mrf)
+    float KronFouter;    ///< Kron flux (flux in 2.5*Mrf)
+
 }
 pmMoments;
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmPCM_MinimizeChisq.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmPCM_MinimizeChisq.c	(revision 29060)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmPCM_MinimizeChisq.c	(revision 29060)
@@ -0,0 +1,447 @@
+/* @file  pmPCM_MinimizeChisq.c
+ * structures and functions to support PSF-convolved model fitting
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.29 $
+ * @date $Date: 2009-02-16 22:30:50 $
+ * Copyright 2010 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <strings.h>
+#include <pslib.h>
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPAMaskWeight.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmSpan.h"
+#include "pmFootprintSpans.h"
+#include "pmFootprint.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
+#include "pmPCMdata.h"
+
+# define FACILITY "psModules.objects"
+
+# define USE_FFT 1
+# define PRE_CONVOLVE 1
+
+bool pmPCM_MinimizeChisq (
+    psMinimization *min,
+    psImage *covar,
+    psVector *params,
+    pmSource *source,
+    pmPCMdata *pcm)
+{
+    psTrace(FACILITY, 3, "---- begin ----\n");
+    PS_ASSERT_PTR_NON_NULL(min, false);
+    PS_ASSERT_VECTOR_NON_NULL(params, false);
+    PS_ASSERT_VECTOR_NON_EMPTY(params, false);
+    PS_ASSERT_VECTOR_TYPE(params, PS_TYPE_F32, false);
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    PS_ASSERT_PTR_NON_NULL(pcm, false);
+    PS_ASSERT_VECTOR_TYPE(pcm->constraint->paramMask, PS_TYPE_VECTOR_MASK, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(params, pcm->constraint->paramMask, false);
+
+    psVector *paramMask = pcm->constraint->paramMask;
+
+    psMinimizeLMLimitFunc checkLimits = pcm->constraint->checkLimits;
+
+    // this function has test values and current values for several things
+    // the current value is in lower case
+    // the test value is in upper case
+
+    // allocate internal arrays (current vs Guess)
+    psImage *Alpha = NULL;
+    psVector *Beta = NULL;
+
+    // Alpha & Beta only contain elements to represent the unmasked parameters
+    if (!psMinLM_AllocAB (&Alpha, &Beta, params, paramMask)) {
+        psAbort ("programming error: no unmasked parameters to be fit\n");
+    }
+
+    // allocate internal arrays (current vs Guess)
+    psImage *alpha   = psImageAlloc(Alpha->numCols, Alpha->numRows, PS_TYPE_F32);
+    psVector *beta   = psVectorAlloc(Beta->n, PS_TYPE_F32);
+    psVector *Params = psVectorAlloc(params->n, PS_TYPE_F32);
+
+    psF32 Chisq = 0.0;
+    psF32 lambda = 0.001;
+    psF32 dLinear = 0.0;
+
+# if (USE_FFT && PRE_CONVOLVE)
+    if (pcm->psfFFT) {
+	psFree (pcm->psfFFT);
+    }
+    pcm->psfFFT = psImageConvolveKernelInit(pcm->modelFlux, pcm->psf);
+# endif    
+
+    // calculate initial alpha and beta, set chisq (min->value)
+    min->value = pmPCM_SetABX(alpha, beta, params, paramMask, pcm, source);
+    if (isnan(min->value)) {
+        min->iter = min->maxIter;
+        return(false);
+    }
+    // dump some useful info if trace is defined
+    if (psTraceGetLevel(FACILITY) >= 6) {
+        p_psImagePrint(psTraceGetDestination(), alpha, "alpha guess (0)");
+        p_psVectorPrint(psTraceGetDestination(), beta, "beta guess (0)");
+    }
+    if (psTraceGetLevel(FACILITY) >= 5) {
+        p_psVectorPrint(psTraceGetDestination(), params, "params guess (0)");
+    }
+
+    // iterate until the tolerance is reached, or give up
+    bool done = (min->iter >= min->maxIter);
+    while (!done) {
+        psTrace("psModules.objects", 5, "Iteration number %d.  (max iterations is %d).\n", min->iter, min->maxIter);
+        psTrace("psModules.objects", 5, "Last delta is %f.  stop if < %f, accept if < %f\n", min->lastDelta, min->minTol, min->maxTol);
+
+        // set a new guess for Alpha, Beta, Params
+        if (!psMinLM_GuessABP(Alpha, Beta, Params, alpha, beta, params, paramMask, checkLimits, lambda, &dLinear)) {
+            min->iter ++;
+	    if (min->iter >=  min->maxIter) break;
+            lambda *= 10.0;
+            continue;
+        }
+
+        // dump some useful info if trace is defined
+        if (psTraceGetLevel(FACILITY) >= 6) {
+            p_psImagePrint(psTraceGetDestination(), Alpha, "Alpha guess (1)");
+            p_psVectorPrint(psTraceGetDestination(), Beta, "Beta guess (1)");
+            p_psVectorPrint(psTraceGetDestination(), beta, "beta current (1)");
+        }
+        if (psTraceGetLevel(FACILITY) >= 5) {
+            p_psVectorPrint(psTraceGetDestination(), Params, "params guess (1)");
+        }
+
+        // calculate Chisq for new guess, update Alpha & Beta
+        Chisq = pmPCM_SetABX(Alpha, Beta, Params, paramMask, pcm, source);
+        if (isnan(Chisq)) {
+            min->iter ++;
+	    if (min->iter >=  min->maxIter) break;
+            lambda *= 10.0;
+            continue;
+        }
+
+        // convergence criterion:
+        // compare the delta (min->value - Chisq) with the
+        // expected delta from the linear model (dLinear)
+        // accept new guess if it is an improvement (rho > 0), or else increase lambda
+        psF32 rho = (min->value - Chisq) / dLinear;
+
+        psTrace(FACILITY, 5, "last chisq: %f, new chisq %f, delta: %f, rho: %f\n", min->value, Chisq, min->lastDelta, rho);
+
+        // dump some useful info if trace is defined
+        if (psTraceGetLevel(FACILITY) >= 6) {
+            p_psImagePrint(psTraceGetDestination(), Alpha, "alpha guess (2)");
+            p_psVectorPrint(psTraceGetDestination(), Beta, "beta guess (2)");
+        }
+
+        /* if (Chisq < min->value) {  */
+        if (rho >= -1e-6) {
+            min->lastDelta = (min->value - Chisq) / (source->pixels->numCols*source->pixels->numRows - params->n);
+            min->value = Chisq;
+            alpha  = psImageCopy(alpha, Alpha, PS_TYPE_F32);
+            beta   = psVectorCopy(beta, Beta, PS_TYPE_F32);
+            params = psVectorCopy(params, Params, PS_TYPE_F32);
+            lambda *= 0.25;
+
+            // save the new convolved model image
+            psFree (source->modelFlux);
+            source->modelFlux = pmPCMdataSaveImage(pcm);
+        } else {
+            lambda *= 10.0;
+        }
+        min->iter++;
+
+	done = (min->iter >= min->maxIter);
+	
+	// check for convergence:
+	float chisqDOF = Chisq / pcm->nDOF;
+	if (!isfinite(min->maxChisqDOF) || ((chisqDOF < min->maxChisqDOF) && isfinite(min->lastDelta))) {
+	    done |= (min->lastDelta < min->minTol);
+	}
+    }
+    psTrace(FACILITY, 5, "chisq: %f, last delta: %f, Niter: %d\n", min->value, min->lastDelta, min->iter);
+
+    // construct & return the covariance matrix (if requested)
+    if (covar != NULL) {
+        if (!psMinLM_GuessABP(Alpha, Beta, Params, alpha, beta, params, paramMask, NULL, 0.0, NULL)) {
+            psTrace (FACILITY, 5, "failure to calculate covariance matrix\n");
+        }
+        // set covar values which are not masked
+        psImageInit (covar, 0.0);
+        for (int j = 0, J = 0; j < params->n; j++) {
+            if (paramMask && (paramMask->data.PS_TYPE_VECTOR_MASK_DATA[j])) {
+                covar->data.F32[j][j] = 1.0;
+                continue;
+            }
+            for (int k = 0, K = 0; k < params->n; k++) {
+                if (paramMask && (paramMask->data.PS_TYPE_VECTOR_MASK_DATA[k])) continue;
+                covar->data.F32[j][k] = Alpha->data.F32[J][K];
+                K++;
+            }
+            J++;
+        }
+    }
+
+    // free the internal temporary data
+    psFree(alpha);
+    psFree(Alpha);
+    psFree(beta);
+    psFree(Beta);
+    psFree(Params);
+
+    // if the last improvement was at least as good as maxTol, accept the fit:
+    if (min->lastDelta <= min->maxTol) {
+	psTrace(FACILITY, 6, "---- end (true) ----\n");
+        return(true);
+    }
+    psTrace(FACILITY, 6, "---- end (false) ----\n");
+    return(false);
+}
+
+psF32 pmPCM_SetABX(
+    psImage  *alpha,
+    psVector *beta,
+    const psVector *params,
+    const psVector *paramMask,
+    pmPCMdata *pcm,
+    const pmSource *source)
+{
+    // XXX: Check vector sizes.
+    PS_ASSERT_IMAGE_NON_NULL(alpha, NAN);
+    PS_ASSERT_VECTOR_NON_NULL(beta, NAN);
+    PS_ASSERT_VECTOR_NON_NULL(params, NAN);
+
+    PS_ASSERT_PTR_NON_NULL(source, NAN);
+    PS_ASSERT_IMAGE_NON_NULL(source->pixels, NAN);
+    PS_ASSERT_IMAGE_NON_NULL(source->variance, NAN);
+    PS_ASSERT_IMAGE_NON_NULL(source->maskObj, NAN);
+
+    PS_ASSERT_VECTOR_TYPE(params, PS_TYPE_F32, false);
+    if (paramMask) {
+        PS_ASSERT_VECTOR_TYPE(paramMask, PS_TYPE_VECTOR_MASK, false);
+    }
+
+    // 1 *** generate the model and derivative images for this parameter set
+
+    // storage for model derivatives
+    psVector *deriv = psVectorAlloc(params->n, PS_TYPE_F32);
+
+    // working vector to store local coordinate
+    psVector *coord = psVectorAlloc(2, PS_TYPE_F32);
+
+    psImageInit (pcm->modelFlux, 0.0);
+    for (int n = 0; n < params->n; n++) {
+        if (!pcm->dmodelsFlux->data[n]) continue;
+        psImageInit (pcm->dmodelsFlux->data[n], 0.0);
+    }
+
+    // fill in the coordinate and value entries
+    for (psS32 i = 0; i < source->pixels->numRows; i++) {
+        for (psS32 j = 0; j < source->pixels->numCols; j++) {
+
+            // XXX can we skip some of the data points where the model
+            // is not going to be fitted??
+
+            // skip masked points
+            // XXX probably should not skipped masked points:
+            // XXX skip if convolution of unmasked pixels will not see this pixel
+            // if (source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[i][j]) {
+            // continue;
+            // }
+
+            // skip zero-variance points
+            // XXX why is this not masked?
+            // if (source->variance->data.F32[i][j] == 0) {
+            // continue;
+            // }
+            // skip nan value points
+            // XXX why is this not masked?
+            // if (!isfinite(source->pixels->data.F32[i][j])) {
+            // continue;
+            // }
+
+            // Convert i/j to image space:
+            coord->data.F32[0] = (psF32) (j + source->pixels->col0);
+            coord->data.F32[1] = (psF32) (i + source->pixels->row0);
+
+            pcm->modelFlux->data.F32[i][j] = pcm->modelConv->modelFunc (deriv, params, coord);
+
+            for (int n = 0; n < params->n; n++) {
+                if ((paramMask != NULL) && (paramMask->data.PS_TYPE_VECTOR_MASK_DATA[n])) { continue; }
+                psImage *dmodel = pcm->dmodelsFlux->data[n];
+                dmodel->data.F32[i][j] = deriv->data.F32[n];
+            }
+        }
+    }
+    psFree(coord);
+    psFree(deriv);
+
+    // convolve model and dmodel arrays with PSF
+    // XXX speed this up by saving the FFTed psf (for each source, obviously)
+
+    // XXX save the FFT'ed psf
+    // XXX create an alternative function which takes a pre-FFTed kernel
+
+# if (USE_FFT)
+# if (PRE_CONVOLVE)
+    // convolve model image and derivative images with pre-convolved kernel
+    psImageConvolveKernel (pcm->modelConvFlux, pcm->modelFlux, NULL, 0, pcm->psfFFT);
+    for (int n = 0; n < pcm->dmodelsFlux->n; n++) {
+        if (pcm->dmodelsFlux->data[n] == NULL) continue;
+	if (pcm->constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[n]) continue;
+        psImage *dmodel = pcm->dmodelsFlux->data[n];
+        psImage *dmodelConv = pcm->dmodelsConvFlux->data[n];
+        psImageConvolveKernel (dmodelConv, dmodel, NULL, 0, pcm->psfFFT);
+    }
+# else
+    // convolve model image and derivative images with psf via FFT
+    psImageConvolveFFT (pcm->modelConvFlux, pcm->modelFlux, NULL, 0, pcm->psf);
+    for (int n = 0; n < pcm->dmodelsFlux->n; n++) {
+        if (pcm->dmodelsFlux->data[n] == NULL) continue;
+	if (pcm->constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[n]) continue;
+        psImage *dmodel = pcm->dmodelsFlux->data[n];
+        psImage *dmodelConv = pcm->dmodelsConvFlux->data[n];
+        psImageConvolveFFT (dmodelConv, dmodel, NULL, 0, pcm->psf);
+    }
+# endif // PRE-CONVOLVE
+# else
+    // convolve model image and derivative images with psf direct
+    psImageConvolveDirect (pcm->modelConvFlux, pcm->modelFlux, pcm->psf);
+    for (int n = 0; n < pcm->dmodelsFlux->n; n++) {
+        if (pcm->dmodelsFlux->data[n] == NULL) continue;
+	if (pcm->constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[n]) continue;
+        psImage *dmodel = pcm->dmodelsFlux->data[n];
+        psImage *dmodelConv = pcm->dmodelsConvFlux->data[n];
+        psImageConvolveDirect (dmodelConv, dmodel, pcm->psf);
+    }
+# endif // USE_FFT
+
+    // XXX TEST : SAVE IMAGES
+# if (SAVE_IMAGES)
+    psphotSaveImage (NULL, pcm->psf->image, "psf.fits");
+    psphotSaveImage (NULL, pcm->modelFlux, "model.fits");
+    psphotSaveImage (NULL, pcm->modelConvFlux, "modelConv.fits");
+    psphotSaveImage (NULL, source->pixels, "obj.fits");
+    psphotSaveImage (NULL, source->maskObj, "mask.fits");
+    psphotSaveImage (NULL, source->variance, "variance.fits");
+# endif
+
+    // 2 *** accumulate alpha & beta
+
+    // zero alpha and beta for summing below
+    psImageInit (alpha, 0.0);
+    psVectorInit (beta, 0.0);
+    float chisq = 0.0;
+
+    for (psS32 i = 0; i < source->pixels->numRows; i++) {
+        for (psS32 j = 0; j < source->pixels->numCols; j++) {
+            // XXX are we doing the right thing with the mask?
+            // skip masked points
+            if (source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[i][j]) {
+                continue;
+            }
+            // skip zero-variance points
+            if (source->variance->data.F32[i][j] == 0) {
+                continue;
+            }
+            // skip nan value points
+            if (!isfinite(source->pixels->data.F32[i][j])) {
+                continue;
+            }
+
+            float ymodel  = pcm->modelConvFlux->data.F32[i][j];
+            float yweight = 1.0 / source->variance->data.F32[i][j];
+            float delta = ymodel - source->pixels->data.F32[i][j];
+
+            chisq += PS_SQR(delta) * yweight;
+
+            if (isnan(delta)) psAbort("nan in delta");
+            if (isnan(chisq)) psAbort("nan in chisq");
+
+            // alpha & beta only contain unmasked elements
+            for (int n1 = 0, N1 = 0; n1 < params->n; n1++) {
+                if ((paramMask != NULL) && (paramMask->data.PS_TYPE_VECTOR_MASK_DATA[n1])) continue;
+                psImage *dmodel = pcm->dmodelsConvFlux->data[n1];
+                float weight = dmodel->data.F32[i][j] * yweight;
+                for (int n2 = 0, N2 = 0; n2 <= n1; n2++) {
+                    if ((paramMask != NULL) && (paramMask->data.PS_TYPE_VECTOR_MASK_DATA[n2])) continue;
+                    dmodel = pcm->dmodelsConvFlux->data[n2];
+                    alpha->data.F32[N1][N2] += weight * dmodel->data.F32[i][j];
+                    N2++;
+                }
+                beta->data.F32[N1] += weight * delta;
+                N1++;
+            }
+        }
+    }
+
+    // calculate lower-left half of alpha
+    for (psS32 j = 1; j < alpha->numCols; j++) {
+        for (psS32 k = 0; k < j; k++) {
+            alpha->data.F32[k][j] = alpha->data.F32[j][k];
+        }
+    }
+
+    return(chisq);
+}
+
+
+/*
+ *
+ * we have a function func(param; value)
+
+ * basic LMM:
+
+ - fill in the data (x, y)
+
+ chisq = SetABX (alpha, beta, params, paramMask, x, y, dy, func)
+
+ while () {
+ GuessABP (Alpha, Beta, Params, alpha, beta, params, paramMask, checkLimits, lambda)
+ dLinear = dLinear(Beta, beta, lambda);
+ chisq = SetABX (alpha, beta, params, paramMask, x, y, dy, func)
+ convergence tests...
+ }
+
+
+
+ ** GuessABP:
+
+ f_c = sum_i (kern_i * func (x_i; p_o))
+
+ df_c/dp_o = d/dp_o [sum_i (kern_i * func (x_i; p_o))]
+
+ df_c/dp_o = sum_i (d/dp_o [kern_i * func (x_i; p_o)])
+
+ df_c/dp_o = sum_i (kern_i * d/dp_o [func (x_i; p_o)])
+
+ - generate image arrays for func, dfunc/dp_j (not masked)
+ - convolve each with psf
+ - measure delta = f_conv - data
+ - etc
+*/
+
+
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmPCMdata.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmPCMdata.c	(revision 29060)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmPCMdata.c	(revision 29060)
@@ -0,0 +1,364 @@
+/* @file  pmPCMdata.c
+ * structures and functions to support PSF-convolved model fitting
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.29 $
+ * @date $Date: 2009-02-16 22:30:50 $
+ * Copyright 2010 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <strings.h>
+#include <pslib.h>
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPAMaskWeight.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmSpan.h"
+#include "pmFootprintSpans.h"
+#include "pmFootprint.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
+#include "pmPCMdata.h"
+
+static void pmPCMdataFree (pmPCMdata *pcm) {
+
+    if (pcm == NULL) return;
+
+    psFree (pcm->modelFlux);
+    psFree (pcm->modelConvFlux);
+    psFree (pcm->dmodelsFlux);
+    psFree (pcm->dmodelsConvFlux);
+
+    psFree (pcm->modelConv);
+    psFree (pcm->psf);
+    psFree (pcm->psfFFT);
+    psFree (pcm->constraint);
+    return;
+}
+
+pmPCMdata *pmPCMdataAlloc (
+    const psVector *params,
+    const psVector *paramMask,
+    pmSource *source) {
+
+    pmPCMdata *pcm = (pmPCMdata *) psAlloc(sizeof(pmPCMdata));
+    psMemSetDeallocator(pcm, (psFreeFunc) pmPCMdataFree);
+
+    // Allocate storage images for raw model and derivative images
+    pcm->modelFlux = psImageCopy (NULL, source->pixels, PS_TYPE_F32);
+    pcm->dmodelsFlux = psArrayAlloc (params->n);
+    for (psS32 n = 0; n < params->n; n++) {
+        pcm->dmodelsFlux->data[n] = NULL;
+        if ((paramMask != NULL) && (paramMask->data.PS_TYPE_VECTOR_MASK_DATA[n])) { continue; }
+        pcm->dmodelsFlux->data[n] = psImageCopy (NULL, source->pixels, PS_TYPE_F32);
+    }
+
+    // Allocate storage images for convolved model and derivative images
+    pcm->modelConvFlux = psImageCopy (NULL, source->pixels, PS_TYPE_F32);
+    pcm->dmodelsConvFlux = psArrayAlloc (params->n);
+    for (psS32 n = 0; n < params->n; n++) {
+        pcm->dmodelsConvFlux->data[n] = NULL;
+        if ((paramMask != NULL) && (paramMask->data.PS_TYPE_VECTOR_MASK_DATA[n])) { continue; }
+        pcm->dmodelsConvFlux->data[n] = psImageCopy (NULL, source->pixels, PS_TYPE_F32);
+    }
+
+    pcm->modelConv = NULL;
+    pcm->psf = NULL;
+    pcm->psfFFT = NULL;
+    pcm->constraint = NULL;
+    pcm->nDOF = 0;
+
+    return pcm;
+}
+
+psImage *pmPCMdataSaveImage (pmPCMdata *pcm) {
+
+    psImage *model = psImageCopy (NULL, pcm->modelConvFlux, PS_TYPE_F32);
+
+    return model;
+}
+
+psKernel *pmPCMkernelFromPSF (pmSource *source, int nPix) {
+
+    assert (source);
+    assert (source->psfImage); // XXX build if needed?
+
+    int x0 = source->peak->xf - source->psfImage->col0;
+    int y0 = source->peak->yf - source->psfImage->row0;
+
+    // need to decide on the size: dynamically? statically?
+    psKernel *psf = psKernelAlloc (-nPix, +nPix, -nPix, +nPix);
+
+    // XXX we should just re-construct a PSF at this location 
+    // psModelAdd (psf->image, NULL, source->modelPSF, PM_MODEL_OP_FULL | PM_MODEL_OP_NORM | PM_MODEL_OP_CENTER);
+  
+    // if the realized PSF for this object does not cover the full kernel, give up for now
+    if (x0 + psf->xMin < 0) goto escape;
+    if (x0 + psf->xMax >= source->psfImage->numCols) goto escape;
+    if (y0 + psf->yMin < 0) goto escape;
+    if (y0 + psf->yMax >= source->psfImage->numRows) goto escape;
+
+    double sum = 0.0;
+    for (int j = psf->yMin; j <= psf->yMax; j++) {
+	for (int i = psf->xMin; i <= psf->xMax; i++) {
+	    double value = source->psfImage->data.F32[y0 + j][x0 + i];
+	    psf->kernel[j][i] = value;
+	    sum += value;
+	}
+    }
+    assert (sum > 0.0);
+
+    // psf must be normalized (integral = 1.0)
+    for (int i = 0; i < psf->image->numRows; i++) {
+	for (int j = 0; j < psf->image->numCols; j++) {
+	    psf->image->data.F32[i][j] /= sum;
+	}
+    }
+
+    return psf;
+
+escape:
+    psFree (psf);
+    return NULL;
+}
+
+pmPCMdata *pmPCMinit(pmSource *source, pmSourceFitOptions *fitOptions, pmModel *model, psImageMaskType maskVal, float psfSize) {
+
+    // make sure we save a cached copy of the psf flux
+    pmSourceCachePSF (source, maskVal);
+
+    // convert the cached cached psf model for this source to a psKernel
+    psKernel *psf = pmPCMkernelFromPSF (source, psfSize);
+    if (!psf) {
+	// NOTE: this only happens if the source is too close to an edge
+        model->flags |= PM_MODEL_STATUS_BADARGS;
+	return NULL;
+    }
+
+# if (USE_DELTA_PSF)
+    psImageInit (psf->image, 0.0);
+    psf->image->data.F32[(int)(0.5*psf->image->numRows)][(int)(0.5*psf->image->numCols)] = 1.0;
+# endif
+
+    // count the number of unmasked pixels:
+    int nPix = 0;
+    for (psS32 i = 0; i < source->pixels->numRows; i++) {
+        for (psS32 j = 0; j < source->pixels->numCols; j++) {
+            // XXX are we doing the right thing with the mask?
+            // skip masked points
+            if (source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[i][j]) {
+                continue;
+            }
+            // skip zero-variance points
+            if (source->variance->data.F32[i][j] == 0) {
+                continue;
+            }
+            // skip nan value points
+            if (!isfinite(source->pixels->data.F32[i][j])) {
+                continue;
+            }
+	    nPix ++;
+	}
+    }    
+
+    psVector *params  = model->params;
+
+    // create the minimization constraints
+    psMinConstraint *constraint = psMinConstraintAlloc();
+    constraint->paramMask = psVectorAlloc (params->n, PS_TYPE_VECTOR_MASK);
+    constraint->checkLimits = model->modelLimits;
+
+    // set parameter mask based on fitting mode
+    int nParams = 0;
+    switch (fitOptions->mode) {
+      case PM_SOURCE_FIT_NORM:
+        // NORM-only model fits only source normalization (Io)
+        nParams = 1;
+        psVectorInit (constraint->paramMask, 1);
+        constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_I0] = 0;
+        break;
+      case PM_SOURCE_FIT_PSF:
+        // PSF model only fits x,y,Io
+        nParams = 3;
+        psVectorInit (constraint->paramMask, 1);
+        constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_I0] = 0;
+        constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_XPOS] = 0;
+        constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_YPOS] = 0;
+        break;
+      case PM_SOURCE_FIT_EXT:
+        // EXT model fits all params (except sky)
+        nParams = params->n - 1;
+        psVectorInit (constraint->paramMask, 0);
+        constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_SKY] = 1;
+        break;
+      case PM_SOURCE_FIT_INDEX:
+        // PSF model only fits Io, index (PAR7) -- only Io for models with < 8 params
+	psVectorInit (constraint->paramMask, 1);
+	constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_I0] = 0;
+        if (params->n == 7) {
+	    nParams = 1;
+	} else {
+	    nParams = 2;
+	    constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_7] = 0;
+	}
+	break;
+      case PM_SOURCE_FIT_NO_INDEX:
+        // PSF model only fits Io, index (PAR7) -- only Io for models with < 8 params
+	psVectorInit (constraint->paramMask, 0);
+	constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_SKY] = 1;
+        if (params->n == 7) {
+	    nParams = params->n - 1;
+	} else {
+	    nParams = params->n - 2;
+	    constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_7] = 1;
+	}
+	break;
+      default:
+	psAbort("invalid fitting mode");
+    }
+
+    if (nPix <  nParams + 1) {
+        psTrace ("psModules.objects", 4, "insufficient valid pixels\n");
+	psFree (psf);
+	psFree (constraint);
+        model->flags |= PM_MODEL_STATUS_BADARGS;
+	return NULL;
+    }
+
+    // generate PCM data storage structure
+    pmPCMdata *pcm = pmPCMdataAlloc (params, constraint->paramMask, source);
+
+    pcm->psf = psf;
+    pcm->modelConv = psMemIncrRefCounter(model);
+    pcm->constraint = constraint;
+
+    pcm->nPix = nPix;
+    pcm->nDOF = nPix - nParams - 1;
+
+    return pcm;
+}
+
+// has the set of fitted terms changed?  has the fitting radius changed?
+bool pmPCMupdate(pmPCMdata *pcm, pmSource *source, pmSourceFitOptions *fitOptions, pmModel *model) {
+
+    bool newWindow = (source->pixels->numRows != pcm->modelFlux->numRows) || (source->pixels->numCols != pcm->modelFlux->numCols);
+
+    // re-count the number of unmasked pixels:
+    if (newWindow) {
+	for (psS32 i = 0; i < source->pixels->numRows; i++) {
+	    for (psS32 j = 0; j < source->pixels->numCols; j++) {
+		// XXX are we doing the right thing with the mask?
+		// skip masked points
+		if (source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[i][j]) {
+		    continue;
+		}
+		// skip zero-variance points
+		if (source->variance->data.F32[i][j] == 0) {
+		    continue;
+		}
+		// skip nan value points
+		if (!isfinite(source->pixels->data.F32[i][j])) {
+		    continue;
+		}
+		pcm->nPix ++;
+	    }
+	}    
+    }
+
+    // if we changed the fit mode, we need to update nDOF
+    int nParams = 0;
+    // set parameter mask based on fitting mode
+    switch (fitOptions->mode) {
+      case PM_SOURCE_FIT_NORM:
+	// NORM-only model fits only source normalization (Io)
+	nParams = 1;
+	psVectorInit (pcm->constraint->paramMask, 1);
+	pcm->constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_I0] = 0;
+	break;
+      case PM_SOURCE_FIT_PSF:
+	// PSF model only fits x,y,Io
+	nParams = 3;
+	psVectorInit (pcm->constraint->paramMask, 1);
+	pcm->constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_I0] = 0;
+	pcm->constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_XPOS] = 0;
+	pcm->constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_YPOS] = 0;
+	break;
+      case PM_SOURCE_FIT_EXT:
+	// EXT model fits all params (except sky)
+	nParams = model->params->n - 1;
+	psVectorInit (pcm->constraint->paramMask, 0);
+	pcm->constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_SKY] = 1;
+	break;
+      case PM_SOURCE_FIT_INDEX:
+	// PSF model only fits Io, index (PAR7) -- only Io for models with < 8 params
+	psVectorInit (pcm->constraint->paramMask, 1);
+	pcm->constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_I0] = 0;
+	if (model->params->n == 7) {
+	    nParams = 1;
+	} else {
+	    nParams = 2;
+	    pcm->constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_7] = 0;
+	}
+	break;
+      case PM_SOURCE_FIT_NO_INDEX:
+	// PSF model only fits Io, index (PAR7) -- only Io for models with < 8 params
+	psVectorInit (pcm->constraint->paramMask, 0);
+	pcm->constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_SKY] = 1;
+	if (model->params->n == 7) {
+	    nParams = model->params->n - 1;
+	} else {
+	    nParams = model->params->n - 2;
+	    pcm->constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_7] = 1;
+	}
+	break;
+      default:
+	psAbort("invalid fitting mode");
+    }
+
+    if (pcm->nPix <  nParams + 1) {
+        psTrace ("psModules.objects", 4, "insufficient valid pixels\n");
+        model->flags |= PM_MODEL_STATUS_BADARGS;
+	return false;
+    }
+    pcm->nDOF = pcm->nPix - nParams - 1;
+
+    // has the source pixel window changed?
+    if (newWindow) {
+
+	// adjust all supporting images:
+	pcm->modelFlux = psImageCopy (pcm->modelFlux, source->pixels, PS_TYPE_F32);
+	for (psS32 n = 0; n < pcm->dmodelsFlux->n; n++) {
+	    if ((pcm->constraint->paramMask != NULL) && (pcm->constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[n])) { continue; }
+	    pcm->dmodelsFlux->data[n] = psImageCopy (pcm->dmodelsFlux->data[n], source->pixels, PS_TYPE_F32);
+	}
+
+	// adjust images for convolved model and derivative images
+	pcm->modelConvFlux = psImageCopy (pcm->modelConvFlux, source->pixels, PS_TYPE_F32);
+	for (psS32 n = 0; n < pcm->dmodelsConvFlux->n; n++) {
+	    if ((pcm->constraint->paramMask != NULL) && (pcm->constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[n])) { continue; }
+	    pcm->dmodelsConvFlux->data[n] = psImageCopy (pcm->dmodelsConvFlux->data[n], source->pixels, PS_TYPE_F32);
+	}
+    }
+
+    return true;
+}
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmPCMdata.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmPCMdata.h	(revision 29060)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmPCMdata.h	(revision 29060)
@@ -0,0 +1,91 @@
+/* @file  pmPCMdata.h
+ * structures and functions to support PSF-convolved model fitting
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.29 $
+ * @date $Date: 2009-02-16 22:30:50 $
+ * Copyright 2010 Institute for Astronomy, University of Hawaii
+ */
+
+# ifndef PM_PCM_DATA_H
+# define PM_PCM_DATA_H
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+/** pmPCMdata : PSF Convolved Model data storage structure
+ *
+ * Structure to carry the data needed to generate a PSF-convolve model fit
+ *
+ */
+// 
+typedef struct {
+    psImage *modelFlux;
+    psArray *dmodelsFlux;
+    psImage *modelConvFlux;
+    psArray *dmodelsConvFlux;
+
+    pmModel *modelConv;
+    psKernel *psf;
+    psKernelFFT *psfFFT;
+
+    psMinConstraint *constraint;
+    int nPix;
+    int nDOF;
+} pmPCMdata;
+
+// structures & functions to support psf-convolved model fitting
+
+// psf-convolved model fitting
+bool psphotModelWithPSF_LMM (
+    psMinimization *min,
+    psImage *covar,
+    psVector *params,
+    psMinConstraint *constraint,
+    pmSource *source,
+    const psKernel *psf,
+    psMinimizeLMChi2Func func);
+
+psF32 psphotModelWithPSF_SetABX(
+    psImage  *alpha,
+    psVector *beta,
+    const psVector *params,
+    const psVector *paramMask,
+    pmPCMdata *pcm,
+    const pmSource *source,
+    const psKernel *psf,
+    psMinimizeLMChi2Func func);
+
+pmPCMdata *pmPCMdataAlloc (
+    const psVector *params,
+    const psVector *paramMask,
+    pmSource *source);
+
+pmPCMdata *pmPCMinit(pmSource *source, pmSourceFitOptions *fitOptions, pmModel *model, psImageMaskType maskVal, float psfSize);
+bool pmPCMupdate(pmPCMdata *pcm, pmSource *source, pmSourceFitOptions *fitOptions, pmModel *model);
+
+psImage *pmPCMdataSaveImage (pmPCMdata *pcm);
+
+psF32 pmPCM_SetABX(
+    psImage  *alpha,
+    psVector *beta,
+    const psVector *params,
+    const psVector *paramMask,
+    pmPCMdata *pcm,
+    const pmSource *source);
+
+bool pmPCM_MinimizeChisq (
+    psMinimization *min,
+    psImage *covar,
+    psVector *params,
+    pmSource *source,
+    pmPCMdata *pcm);
+
+bool pmSourceModelGuessPCM (pmPCMdata *pcm, pmSource *source, psImageMaskType maskVal, psImageMaskType markVal);
+
+bool pmSourceFitPCM (pmPCMdata *pcm, pmSource *source, pmSourceFitOptions *fitOptions, psImageMaskType maskVal, psImageMaskType markVal, int psfSize);
+
+
+/// @}
+# endif /* PM_PCM_DATA_H */
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmPSF.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmPSF.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmPSF.c	(revision 29060)
@@ -25,19 +25,28 @@
 #include "pmHDU.h"
 #include "pmFPA.h"
+#include "pmFPAMaskWeight.h"
+#include "psVectorBracket.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmResiduals.h"
-#include "pmGrowthCurve.h"
-#include "pmTrend2D.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
-#include "pmSource.h"
-#include "pmModelClass.h"
-#include "pmModelUtils.h"
-#include "pmSourcePhotometry.h"
-#include "pmFPAMaskWeight.h"
-#include "psVectorBracket.h"
+#include "pmPSFtry.h"
+#include "pmDetections.h"
+
 #include "pmErrorCodes.h"
 
@@ -54,4 +63,5 @@
 
     psFree (options->stats);
+    psFree (options->fitOptions);
     return;
 }
@@ -65,4 +75,5 @@
 
     options->stats         = NULL;
+    options->fitOptions    = NULL; // XXX this has to be set before calling pmPSF fit functions
 
     options->psfTrendMode  = PM_TREND_NONE;
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmPSF.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmPSF.h	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmPSF.h	(revision 29060)
@@ -14,15 +14,6 @@
 # define PM_PSF_H
 
-#include <pslib.h>
-#include "pmTrend2D.h"
-#include "pmGrowthCurve.h"
-#include "pmResiduals.h"
-#include "pmFPA.h"
-
 /// @addtogroup Objects Object Detection / Analysis Functions
 /// @{
-
-// type of model carried by the pmModel structure
-typedef int pmModelType;
 
 /** pmPSF data structure
@@ -38,5 +29,5 @@
  *
  */
-typedef struct {
+struct pmPSF {
     pmModelType type;                   ///< PSF Model in use
     psArray *params;                    ///< Model parameters (psPolynomial2D)
@@ -64,5 +55,5 @@
     pmGrowthCurve *growth;              ///< apMag vs Radius
     pmResiduals *residuals;             ///< normalized residual image (no spatial variation)
-} pmPSF;
+};
 
 typedef struct {
@@ -82,4 +73,5 @@
     float         apRadius;
     bool          chiFluxTrend;         // Fit a trend in Chi2 as a function of flux?
+    pmSourceFitOptions *fitOptions;
 } pmPSFOptions;
 
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmPSF_IO.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmPSF_IO.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmPSF_IO.c	(revision 29060)
@@ -38,17 +38,26 @@
 #include "pmFPAfileFitsIO.h"
 
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmGrowthCurve.h"
-#include "pmResiduals.h"
-#include "pmTrend2D.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
+#include "pmPSFtry.h"
+#include "pmDetections.h"
+
 #include "pmPSF_IO.h"
-#include "pmSource.h"
-#include "pmModelClass.h"
-#include "pmModelUtils.h"
 #include "pmSourceIO.h"
 
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmPSFtry.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmPSFtry.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmPSFtry.c	(revision 29060)
@@ -19,19 +19,26 @@
 #include "pmFPA.h"
 #include "pmFPAMaskWeight.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmResiduals.h"
-#include "pmGrowthCurve.h"
-#include "pmTrend2D.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
-#include "pmSource.h"
-#include "pmSourceUtils.h"
 #include "pmPSFtry.h"
-#include "pmModelClass.h"
-#include "pmModelUtils.h"
-#include "pmSourceFitModel.h"
+#include "pmDetections.h"
+
 #include "pmSourcePhotometry.h"
 #include "pmSourceVisual.h"
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmPSFtryFitEXT.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmPSFtryFitEXT.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmPSFtryFitEXT.c	(revision 29060)
@@ -19,19 +19,27 @@
 #include "pmFPA.h"
 #include "pmFPAMaskWeight.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmResiduals.h"
-#include "pmGrowthCurve.h"
-#include "pmTrend2D.h"
-#include "pmPSF.h"
+#include "pmModelFuncs.h"
 #include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
 #include "pmSource.h"
 #include "pmSourceUtils.h"
+#include "pmSourceFitModel.h"
+#include "pmPSF.h"
 #include "pmPSFtry.h"
-#include "pmModelClass.h"
-#include "pmModelUtils.h"
-#include "pmSourceFitModel.h"
+#include "pmDetections.h"
+
 #include "pmSourcePhotometry.h"
 #include "pmSourceVisual.h"
@@ -44,4 +52,7 @@
 
     psTimerStart ("psf.fit");
+
+    // in this segment, we are fitting the full PSF model class (shape unconstrained)
+    options->fitOptions->mode = PM_SOURCE_FIT_EXT;
 
     // maskVal is used to test for rejected pixels, and must include markVal
@@ -73,5 +84,5 @@
 
         // fit model as EXT, not PSF
-        status = pmSourceFitModel (source, source->modelEXT, PM_SOURCE_FIT_EXT, maskVal);
+        status = pmSourceFitModel (source, source->modelEXT, options->fitOptions, maskVal);
 
         // clear object mask to define valid pixels
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmPSFtryFitPSF.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmPSFtryFitPSF.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmPSFtryFitPSF.c	(revision 29060)
@@ -17,19 +17,26 @@
 #include "pmFPA.h"
 #include "pmFPAMaskWeight.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmResiduals.h"
-#include "pmGrowthCurve.h"
-#include "pmTrend2D.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
-#include "pmSource.h"
-#include "pmSourceUtils.h"
 #include "pmPSFtry.h"
-#include "pmModelClass.h"
-#include "pmModelUtils.h"
-#include "pmSourceFitModel.h"
+#include "pmDetections.h"
+
 #include "pmSourcePhotometry.h"
 #include "pmSourceVisual.h"
@@ -42,4 +49,7 @@
 
     psTimerStart ("psf.fit");
+
+    // in this segment, we are fitting the fitted PSF model class (shape constrained)
+    options->fitOptions->mode = PM_SOURCE_FIT_PSF;
 
     // maskVal is used to test for rejected pixels, and must include markVal
@@ -81,5 +91,5 @@
 
         // fit the PSF model to the source
-        status = pmSourceFitModel (source, source->modelPSF, PM_SOURCE_FIT_NORM, maskVal);
+        status = pmSourceFitModel (source, source->modelPSF, options->fitOptions, maskVal);
 
         // skip poor fits
@@ -98,5 +108,5 @@
 
 	// This function calculates the psf and aperture magnitudes
-        status = pmSourceMagnitudes (source, psfTry->psf, PM_SOURCE_PHOT_INTERP, maskVal); // raw PSF mag, AP mag
+        status = pmSourceMagnitudes (source, psfTry->psf, PM_SOURCE_PHOT_INTERP, maskVal, markVal); // raw PSF mag, AP mag
         if (!status || isnan(source->apMag)) {
             psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(markVal)); // clear the circular mask
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmPSFtryMakePSF.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmPSFtryMakePSF.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmPSFtryMakePSF.c	(revision 29060)
@@ -18,19 +18,26 @@
 #include "pmFPA.h"
 #include "pmFPAMaskWeight.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmResiduals.h"
-#include "pmGrowthCurve.h"
-#include "pmTrend2D.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
-#include "pmSource.h"
-#include "pmSourceUtils.h"
 #include "pmPSFtry.h"
-#include "pmModelClass.h"
-#include "pmModelUtils.h"
-#include "pmSourceFitModel.h"
+#include "pmDetections.h"
+
 #include "pmSourcePhotometry.h"
 #include "pmSourceVisual.h"
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmPSFtryMetric.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmPSFtryMetric.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmPSFtryMetric.c	(revision 29060)
@@ -18,19 +18,26 @@
 #include "pmFPA.h"
 #include "pmFPAMaskWeight.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmResiduals.h"
-#include "pmGrowthCurve.h"
-#include "pmTrend2D.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
-#include "pmSource.h"
-#include "pmSourceUtils.h"
 #include "pmPSFtry.h"
-#include "pmModelClass.h"
-#include "pmModelUtils.h"
-#include "pmSourceFitModel.h"
+#include "pmDetections.h"
+
 #include "pmSourcePhotometry.h"
 #include "pmSourceVisual.h"
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmPSFtryModel.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmPSFtryModel.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmPSFtryModel.c	(revision 29060)
@@ -19,20 +19,26 @@
 #include "pmFPA.h"
 #include "pmFPAMaskWeight.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmResiduals.h"
-#include "pmGrowthCurve.h"
-#include "pmTrend2D.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
-#include "pmSource.h"
-#include "pmSourceUtils.h"
 #include "pmPSFtry.h"
-#include "pmModelClass.h"
-#include "pmModelUtils.h"
-#include "pmSourceFitModel.h"
-#include "pmSourcePhotometry.h"
+#include "pmDetections.h"
+
 #include "pmSourceVisual.h"
 
@@ -49,4 +55,6 @@
 pmPSFtry *pmPSFtryModel (const psArray *sources, const char *modelName, pmPSFOptions *options, psImageMaskType maskVal, psImageMaskType markVal)
 {
+    assert (options->fitOptions);
+
     // validate the requested model name
     options->type = pmModelClassGetType (modelName);
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmPeaks.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmPeaks.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmPeaks.c	(revision 29060)
@@ -22,4 +22,5 @@
 #include <pslib.h>
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmPeaks.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmPeaks.h	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmPeaks.h	(revision 29060)
@@ -17,7 +17,4 @@
 # ifndef PM_PEAKS_H
 # define PM_PEAKS_H
-
-#include <pslib.h>
-#include "pmFootprint.h"
 
 /// @addtogroup Objects Object Detection / Analysis Functions
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmPhotObj.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmPhotObj.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmPhotObj.c	(revision 29060)
@@ -16,6 +16,27 @@
 #include <string.h>
 #include <pslib.h>
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmSpan.h"
+#include "pmFootprintSpans.h"
+#include "pmFootprint.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+
 #include "pmPhotObj.h"
-#include "pmSource.h"
+
 
 static void pmPhotObjFree (pmPhotObj *tmp)
@@ -82,2 +103,17 @@
     return (0);
 }
+
+// sort by X (ascending)
+int pmPhotObjSortByX (const void **a, const void **b)
+{
+    pmPhotObj *objA = *(pmPhotObj **)a;
+    pmPhotObj *objB = *(pmPhotObj **)b;
+
+    psF32 fA = objA->x;
+    psF32 fB = objB->x;
+
+    psF32 diff = fA - fB;
+    if (diff > FLT_EPSILON) return (+1);
+    if (diff < FLT_EPSILON) return (-1);
+    return (0);
+}
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmPhotObj.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmPhotObj.h	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmPhotObj.h	(revision 29060)
@@ -10,9 +10,4 @@
 # ifndef PM_PHOT_OBJ_H
 # define PM_PHOT_OBJ_H
-
-#include <pslib.h>
-#include "pmPeaks.h"
-#include "pmModel.h"
-#include "pmMoments.h"
 
 /// @addtogroup Objects Object Detection / Analysis Functions
@@ -47,4 +42,5 @@
 
 int pmPhotObjSortBySN (const void **a, const void **b);
+int pmPhotObjSortByX (const void **a, const void **b);
 
 /// @}
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSource.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSource.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSource.c	(revision 29060)
@@ -23,13 +23,20 @@
 #include "pmFPA.h"
 #include "pmFPAMaskWeight.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmResiduals.h"
-#include "pmGrowthCurve.h"
-#include "pmTrend2D.h"
-#include "pmPSF.h"
+#include "pmModelFuncs.h"
 #include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
 #include "pmSource.h"
 
@@ -98,5 +105,6 @@
     static int id = 1;
     pmSource *source = (pmSource *) psAlloc(sizeof(pmSource));
-    *(int *)&source->id = id++;
+    P_PM_SOURCE_SET_ID(source, id++);
+
     source->seq = -1;
     source->peak = NULL;
@@ -114,4 +122,5 @@
     source->type = PM_SOURCE_TYPE_UNKNOWN;
     source->mode = PM_SOURCE_MODE_DEFAULT;
+    source->mode2 = PM_SOURCE_MODE_DEFAULT;
     source->tmpFlags = 0;
     source->extpars = NULL;
@@ -131,5 +140,6 @@
     source->sky    = NAN;
     source->skyErr = NAN;
-    source->pixWeight = NAN;
+    source->pixWeightNotBad = NAN;
+    source->pixWeightNotPoor = NAN;
 
     source->psfChisq = NAN;
@@ -142,8 +152,56 @@
 
 /******************************************************************************
-pmSourceCopy(): copy the pmSource structure and contents
-XXX : are we OK with incrementing the ID?
+pmSourceCopy(): copy the pmSource, yielding a copy of the source that can be used without
+affecting the original.  This Copy can be used to allow multiple fit attempts on the same
+object.  The pixels, variance, and mask arrays all point to the same original subarrays.  The
+peak and moments point at the original values.
 *****************************************************************************/
 pmSource *pmSourceCopy(pmSource *in)
+{
+    if (in == NULL) {
+        return(NULL);
+    }
+    pmSource *source = pmSourceAlloc ();
+
+    // keep the original ID so we can find map back to the original
+    P_PM_SOURCE_SET_ID(source, in->id);
+
+    // peak has the same values as the original
+    if (in->peak != NULL) {
+        source->peak = pmPeakAlloc (in->peak->x, in->peak->y, in->peak->value, in->peak->type);
+        source->peak->xf = in->peak->xf;
+        source->peak->yf = in->peak->yf;
+        source->peak->flux = in->peak->flux;
+        source->peak->SN = in->peak->SN;
+    }
+
+    // copy the values in the moments structure
+    if (in->moments != NULL) {
+        source->moments  =  pmMomentsAlloc();
+        *source->moments = *in->moments;
+    }
+
+    // These images are all views to the parent.  We want a new view, but pointing at the same
+    // pixels.  Modifying these pixels (ie, subtracting the model) will affect the pixels seen
+    // by all copies.
+    source->pixels   = psImageCopyView(NULL, in->pixels);
+    source->variance   = psImageCopyView(NULL, in->variance);
+    source->maskView = in->maskView ? psImageCopyView(NULL, in->maskView) : NULL;
+
+    // the maskObj is a unique mask array; create a new mask image
+    source->maskObj = in->maskObj ? psImageCopy (NULL, in->maskObj, PS_TYPE_IMAGE_MASK) : NULL;
+
+    source->type = in->type;
+    source->mode = in->mode;
+    source->imageID = in->imageID;
+
+    return(source);
+}
+
+/******************************************************************************
+pmSourceCopyData(): this creates a new, duplicate source with the same parameters as the
+original (but is actually a new source at the same location)
+*****************************************************************************/
+pmSource *pmSourceCopyData(pmSource *in)
 {
     if (in == NULL) {
@@ -482,5 +540,5 @@
         }
         psfClump.X  = stats->clippedMean;
-        psfClump.dX = stats->clippedStdev;
+        psfClump.dX = hypot(stats->clippedStdev, PSF_CLUMP_GRID_SCALE);
 
         if (!psVectorStats (stats, tmpSy, NULL, NULL, 0)) {
@@ -489,5 +547,5 @@
         }
         psfClump.Y  = stats->clippedMean;
-        psfClump.dY = stats->clippedStdev;
+        psfClump.dY = hypot(stats->clippedStdev, PSF_CLUMP_GRID_SCALE);
 
         psTrace ("psModules.objects", 2, "clump  X,  Y: %f, %f\n", psfClump.X, psfClump.Y);
@@ -910,5 +968,6 @@
     bool addNoise = mode & PM_MODEL_OP_NOISE;
 
-    if (source->modelFlux) {
+    // require the use of pmModelAddWithOffset if we are adding noise (because the model size and norm are rescaled)
+    if (!addNoise && source->modelFlux) {
         // add in the pixels from the modelFlux image
         int dX = source->modelFlux->col0 - source->pixels->col0;
@@ -931,8 +990,4 @@
 
         psF32 **target = source->pixels->data.F32;
-        if (addNoise) {
-	    // when adding noise, we assume the shape and Io have been modified
-            target = source->variance->data.F32;
-        }
 
         for (int iy = 0; iy < source->modelFlux->numRows; iy++) {
@@ -949,10 +1004,8 @@
             }
         }
-	if (!addNoise) {
-	    if (add) {
-		source->tmpFlags &= ~PM_SOURCE_TMPF_SUBTRACTED;
-	    } else {
-		source->tmpFlags |= PM_SOURCE_TMPF_SUBTRACTED;
-	    }
+	if (add) {
+	    source->tmpFlags &= ~PM_SOURCE_TMPF_SUBTRACTED;
+	} else {
+	    source->tmpFlags |= PM_SOURCE_TMPF_SUBTRACTED;
 	}
         return true;
@@ -973,4 +1026,78 @@
 	}
     }
+
+    return true;
+}
+
+// should we call pmSourceCacheModel if it does not exist?
+bool pmSourceNoiseOp (pmSource *source, pmModelOpMode mode, float FACTOR, float SIZE, bool add, psImageMaskType maskVal, int dx, int dy)
+{
+    assert (mode & PM_MODEL_OP_NOISE);
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    PS_ASSERT_PTR_NON_NULL(source->peak, false);
+
+    if (add) {
+        psTrace ("psphot", 3, "adding noise to object at %f,%f\n", source->peak->xf, source->peak->yf);
+    } else {
+        psTrace ("psphot", 3, "removing noise from object at %f,%f\n", source->peak->xf, source->peak->yf);
+    }
+
+    pmSourceNoiseOpModel (source->modelPSF, source, mode, FACTOR, SIZE, add, maskVal, dx, dy);
+
+    if (source->modelEXT) {
+	pmSourceNoiseOpModel (source->modelEXT, source, mode, FACTOR, SIZE, add, maskVal, dx, dy);
+    }
+
+    return true;
+}
+
+bool pmSourceNoiseOpModel (pmModel *model, pmSource *source, pmModelOpMode mode, float FACTOR, float SIZE, bool add, psImageMaskType maskVal, int dx, int dy) 
+{
+    bool status;
+    psEllipseShape oldshape;
+    psEllipseShape newshape;
+    psEllipseAxes axes;
+
+    if (add) {
+	psTrace ("psphot", 4, "adding noise for object at %f,%f\n", model->params->data.F32[PM_PAR_XPOS], model->params->data.F32[PM_PAR_YPOS]);
+    } else {
+	psTrace ("psphot", 4, "remove noise for object at %f,%f\n", model->params->data.F32[PM_PAR_XPOS], model->params->data.F32[PM_PAR_YPOS]);
+    }
+
+    psF32 *PAR = model->params->data.F32;
+
+    // save original values
+    float oldI0  = PAR[PM_PAR_I0];
+    oldshape.sx  = PAR[PM_PAR_SXX];
+    oldshape.sy  = PAR[PM_PAR_SYY];
+    oldshape.sxy = PAR[PM_PAR_SXY];
+
+    // XXX can this be done more intelligently?
+    if (oldI0 == 0.0) return false;
+    if (!isfinite(oldI0)) return false;
+
+    // increase size and height of source
+    axes = psEllipseShapeToAxes (oldshape, 20.0);
+    axes.major *= SIZE;
+    axes.minor *= SIZE;
+    newshape = psEllipseAxesToShape (axes);
+    PAR[PM_PAR_I0]  = FACTOR*oldI0;
+    PAR[PM_PAR_SXX] = newshape.sx;
+    PAR[PM_PAR_SYY] = newshape.sy;
+    PAR[PM_PAR_SXY] = newshape.sxy;
+
+    psImage *target = source->variance;
+
+    if (add) {
+	status = pmModelAddWithOffset (target, source->maskObj, model, mode, maskVal, dx, dy);
+    } else {
+	status = pmModelSubWithOffset (target, source->maskObj, model, mode, maskVal, dx, dy);
+    }
+
+    // restore original values
+    PAR[PM_PAR_I0]  = oldI0;
+    PAR[PM_PAR_SXX] = oldshape.sx;
+    PAR[PM_PAR_SYY] = oldshape.sy;
+    PAR[PM_PAR_SXY] = oldshape.sxy;
 
     return true;
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSource.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSource.h	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSource.h	(revision 29060)
@@ -11,15 +11,6 @@
 # define PM_SOURCE_H
 
-#include <pslib.h>
-#include "pmPeaks.h"
-#include "pmModel.h"
-#include "pmMoments.h"
-#include "pmSourceExtendedPars.h"
-#include "pmSourceDiffStats.h"
-
 /// @addtogroup Objects Object Detection / Analysis Functions
 /// @{
-
-#include <pmSourceMasks.h>
 
 /** pmSourceType enumeration
@@ -74,4 +65,5 @@
     pmSourceType type;                  ///< Best identification of object.
     pmSourceMode mode;                  ///< analysis flags set for object.
+    pmSourceMode2 mode2;                ///< analysis flags set for object.
     pmSourceTmpF tmpFlags;              ///< internal-only flags
     psArray *blends;                    ///< collection of sources thought to be confused with object
@@ -82,10 +74,14 @@
     float errMag;                       ///< error in psfMag OR extMag (depending on type)
     float apMag;                        ///< apMag corresponding to psfMag or extMag (depending on type)
-    float pixWeight;                    ///< model-weighted coverage of valid pixels
+    float apMagRaw;                     ///< raw mag in given aperture
+    float apRadius;			///< radius for aperture magnitude
+
+    float pixWeightNotBad;              ///< PSF-weighted coverage of unmasked (not BAD) pixels
+    float pixWeightNotPoor;             ///< PSF-weighted coverage of unmasked (not POOR) pixels
+
     float psfChisq;                     ///< probability of PSF
     float crNsigma;                     ///< Nsigma deviation from PSF to CR
     float extNsigma;                    ///< Nsigma deviation from PSF to EXT
     float sky, skyErr;                  ///< The sky and its error at the center of the object
-    float apRadius;
     psRegion region;                    ///< area on image covered by selected pixels
     pmSourceExtendedPars *extpars;      ///< extended source parameters
@@ -113,4 +109,6 @@
 pmPSFClump;
 
+// private macro to set the source ID (a const)
+#define P_PM_SOURCE_SET_ID(S,V) { *(int *)&(S)->id = (V); }
 
 /** pmSourceAlloc()
@@ -126,4 +124,5 @@
 
 pmSource  *pmSourceCopy(pmSource *source);
+pmSource *pmSourceCopyData(pmSource *in);
 
 // free just the pixels for a source, keeping derived data
@@ -242,4 +241,7 @@
 bool pmSourceSubWithOffset (pmSource *source, pmModelOpMode mode, psImageMaskType maskVal, int dx, int dy);
 
+bool pmSourceNoiseOpModel (pmModel *model, pmSource *source, pmModelOpMode mode, float FACTOR, float SIZE, bool add, psImageMaskType maskVal, int dx, int dy);
+bool pmSourceNoiseOp (pmSource *source, pmModelOpMode mode, float FACTOR, float SIZE, bool add, psImageMaskType maskVal, int dx, int dy);
+
 bool pmSourceOp (pmSource *source, pmModelOpMode mode, bool add, psImageMaskType maskVal, int dx, int dy);
 bool pmSourceCacheModel (pmSource *source, psImageMaskType maskVal);
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceContour.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceContour.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceContour.c	(revision 29060)
@@ -23,14 +23,22 @@
 #include "pmHDU.h"
 #include "pmFPA.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmResiduals.h"
-#include "pmGrowthCurve.h"
-#include "pmTrend2D.h"
-#include "pmPSF.h"
+#include "pmModelFuncs.h"
 #include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
 #include "pmSource.h"
+
 #include "pmSourceContour.h"
 
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceDiffStats.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceDiffStats.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceDiffStats.c	(revision 29060)
@@ -29,4 +29,9 @@
     diffStats->nRatioAll = NAN;
     diffStats->nGood = 0;
+
+    diffStats->SNp = NAN;
+    diffStats->SNm = NAN;
+    diffStats->Rp = NAN;
+    diffStats->Rm = NAN;
 }
 
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceDiffStats.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceDiffStats.h	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceDiffStats.h	(revision 29060)
@@ -29,4 +29,8 @@
     float nRatioAll;			// = nGood / (nGood + nMask + nBad)
     int   nGood;			// nGood as defined above
+    float SNp;				// S/N of matched source in positive image
+    float SNm;				// S/N of matched source in negative image
+    float Rp;				// radius of matched source in positive image
+    float Rm;				// radius of matched source in negative image
 } pmSourceDiffStats;
 
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceFitModel.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceFitModel.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceFitModel.c	(revision 29060)
@@ -23,37 +23,47 @@
 #include "pmHDU.h"
 #include "pmFPA.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmGrowthCurve.h"
-#include "pmResiduals.h"
-#include "pmTrend2D.h"
-#include "pmPSF.h"
+#include "pmModelFuncs.h"
 #include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
 #include "pmSource.h"
-#include "pmModelClass.h"
 #include "pmSourceFitModel.h"
 
-// save as static values so they may be set externally
-static psF32 PM_SOURCE_FIT_MODEL_NUM_ITERATIONS = 15;
-static psF32 PM_SOURCE_FIT_MODEL_TOLERANCE = 0.1;
-static psF32 PM_SOURCE_FIT_MODEL_WEIGHT = 1.0;
-static bool  PM_SOURCE_FIT_MODEL_PIX_WEIGHTS = true;
-
-bool pmSourceFitModelInit (float nIter, float tol, float weight, bool poissonErrors)
+void pmSourceFitOptionsFree(pmSourceFitOptions *opt)
 {
-
-    PM_SOURCE_FIT_MODEL_NUM_ITERATIONS = nIter;
-    PM_SOURCE_FIT_MODEL_TOLERANCE = tol;
-    PM_SOURCE_FIT_MODEL_WEIGHT = weight;
-    PM_SOURCE_FIT_MODEL_PIX_WEIGHTS = poissonErrors;
-
-    return true;
+    return;
+}
+
+pmSourceFitOptions *pmSourceFitOptionsAlloc(void) {
+
+    pmSourceFitOptions *opt = (pmSourceFitOptions *) psAlloc(sizeof(pmSourceFitOptions));
+    psMemSetDeallocator(opt, (psFreeFunc) pmSourceFitOptionsFree);
+
+    opt->mode = PM_SOURCE_FIT_PSF;
+    opt->nIter  = 15;
+    opt->minTol = 0.01;
+    opt->maxTol = 1.00;
+    opt->weight = 1.00;
+    opt->maxChisqDOF = NAN;
+    opt->poissonErrors = true;
+
+    return opt;
 }
 
 bool pmSourceFitModel (pmSource *source,
                        pmModel *model,
-                       pmSourceFitMode mode,
+                       pmSourceFitOptions *options,
                        psImageMaskType maskVal)
 {
@@ -76,4 +86,9 @@
     psVector *yErr = psVectorAllocEmpty(nPix, PS_TYPE_F32);
 
+    // XXX for a test, skip the central pixel in the sersic fit
+    bool skipCenter = false && (model->type == pmModelClassGetType("PS_MODEL_SERSIC"));
+    float Xo = model->params->data.F32[PM_PAR_XPOS];
+    float Yo = model->params->data.F32[PM_PAR_YPOS];
+
     // fill in the coordinate and value entries
     nPix = 0;
@@ -95,14 +110,24 @@
             // skip nan values in image
             if (!isfinite(source->variance->data.F32[i][j])) {
-	      fprintf (stderr, "impossible! %x vs %x\n", source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[i][j], maskVal);
-	      continue;
-            }
-
-            psVector *coord = psVectorAlloc(2, PS_TYPE_F32);
+		fprintf (stderr, "impossible! %x vs %x\n", source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[i][j], maskVal);
+		continue;
+            }
 
             // Convert i/j to image space:
 	    // 0.5 PIX: the coordinate values must be in pixel coords, not index	    
-            coord->data.F32[0] = (psF32) (j + 0.5 + source->pixels->col0);
-            coord->data.F32[1] = (psF32) (i + 0.5 + source->pixels->row0);
+            float Xv = (psF32) (j + 0.5 + source->pixels->col0);
+            float Yv = (psF32) (i + 0.5 + source->pixels->row0);
+
+	    // XXX possible skip of center pixel:
+	    if (skipCenter) {
+		float r = hypot(Xv - Xo, Yv - Yo);
+		if (r < 0.75) {
+		    continue;
+		}
+	    }
+
+            psVector *coord = psVectorAlloc(2, PS_TYPE_F32);
+            coord->data.F32[0] = Xv;
+            coord->data.F32[1] = Yv;
             x->data[nPix] = (psPtr *) coord;
             y->data.F32[nPix] = source->pixels->data.F32[i][j];
@@ -111,8 +136,8 @@
             // as variance to avoid the bias from systematic errors here we would just use the
             // source sky variance
-            if (PM_SOURCE_FIT_MODEL_PIX_WEIGHTS) {
+            if (options->poissonErrors) {
                 yErr->data.F32[nPix] = 1.0 / source->variance->data.F32[i][j];
             } else {
-                yErr->data.F32[nPix] = 1.0 / PM_SOURCE_FIT_MODEL_WEIGHT;
+                yErr->data.F32[nPix] = 1.0 / options->weight;
             }
             nPix++;
@@ -133,6 +158,6 @@
     // set parameter mask based on fitting mode
     int nParams = 0;
-    switch (mode) {
-    case PM_SOURCE_FIT_NORM:
+    switch (options->mode) {
+      case PM_SOURCE_FIT_NORM:
         // NORM-only model fits only source normalization (Io)
         nParams = 1;
@@ -140,5 +165,5 @@
         constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_I0] = 0;
         break;
-    case PM_SOURCE_FIT_PSF:
+      case PM_SOURCE_FIT_PSF:
         // PSF model only fits x,y,Io
         nParams = 3;
@@ -148,5 +173,5 @@
         constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_YPOS] = 0;
         break;
-    case PM_SOURCE_FIT_EXT:
+      case PM_SOURCE_FIT_EXT:
         // EXT model fits all params (except sky)
         nParams = params->n - 1;
@@ -154,11 +179,33 @@
         constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_SKY] = 1;
         break;
-    default:
-        psAbort("invalid fitting mode");
+      case PM_SOURCE_FIT_INDEX:
+        // PSF model only fits Io, index (PAR7) -- only Io for models with < 8 params
+	psVectorInit (constraint->paramMask, 1);
+	constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_I0] = 0;
+        if (params->n == 7) {
+	    nParams = 1;
+	} else {
+	    nParams = 2;
+	    constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_7] = 0;
+	}
+	break;
+      case PM_SOURCE_FIT_NO_INDEX:
+        // PSF model only fits Io, index (PAR7) -- only Io for models with < 8 params
+	psVectorInit (constraint->paramMask, 0);
+	constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_SKY] = 1;
+        if (params->n == 7) {
+	    nParams = params->n - 1;
+	} else {
+	    nParams = params->n - 2;
+	    constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_7] = 1;
+	}
+	break;
+      default:
+	psAbort("invalid fitting mode");
     }
     // force the floating parameters to fall within the contraint ranges
     for (int i = 0; i < params->n; i++) {
-        model->modelLimits (PS_MINIMIZE_PARAM_MIN, i, params->data.F32, NULL);
-        model->modelLimits (PS_MINIMIZE_PARAM_MAX, i, params->data.F32, NULL);
+	model->modelLimits (PS_MINIMIZE_PARAM_MIN, i, params->data.F32, NULL);
+	model->modelLimits (PS_MINIMIZE_PARAM_MAX, i, params->data.F32, NULL);
     }
 
@@ -173,5 +220,5 @@
     }
 
-    psMinimization *myMin = psMinimizationAlloc (PM_SOURCE_FIT_MODEL_NUM_ITERATIONS, PM_SOURCE_FIT_MODEL_TOLERANCE);
+    psMinimization *myMin = psMinimizationAlloc (options->nIter, options->minTol, options->maxTol);
 
     psImage *covar = psImageAlloc (params->n, params->n, PS_TYPE_F32);
@@ -194,4 +241,5 @@
     model->flags |= PM_MODEL_STATUS_FITTED;
     if (!fitStatus) model->flags |= PM_MODEL_STATUS_NONCONVERGE;
+    if (myMin->lastDelta > myMin->minTol) model->flags |= PM_MODEL_STATUS_WEAK_FIT;
 
     // get the Gauss-Newton distance for fixed model parameters
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceFitModel.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceFitModel.h	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceFitModel.h	(revision 29060)
@@ -19,13 +19,29 @@
     PM_SOURCE_FIT_EXT,
     PM_SOURCE_FIT_PSF_AND_SKY,
-    PM_SOURCE_FIT_EXT_AND_SKY
+    PM_SOURCE_FIT_EXT_AND_SKY,
+    PM_SOURCE_FIT_INDEX,
+    PM_SOURCE_FIT_NO_INDEX,
 } pmSourceFitMode;
 
-bool pmSourceFitModelInit(
-    float nIter,   ///< max number of allowed iterations
-    float tol,      ///< convergence criterion
-    float weight,      ///< use this weight for constant-weight fits
-    bool poissonErrors   // use poisson errors for fits?
-);
+typedef struct {
+    pmSourceFitMode mode;		///< optionally fit all or a subset of parameters
+    float nIter;			///< max number of allowed iterations
+    float minTol;			///< convergence criterion
+    float maxTol;			///< convergence criterion
+    float maxChisqDOF;			///< convergence criterion
+    float weight;			///< use this weight for constant-weight fits
+    bool poissonErrors;			///< use poisson errors for fits?
+} pmSourceFitOptions;
+
+// the pmSourceFitOptions structure is used to control details of the fitting process
+pmSourceFitOptions *pmSourceFitOptionsAlloc(void);
+
+// bool pmSourceFitModelInit(
+//     pmSourceFitMode mode,		///< what parameter set should be fitted?
+//     float nIter,			///< max number of allowed iterations
+//     float tol,				///< convergence criterion
+//     float weight,			///< use this weight for constant-weight fits
+//     bool poissonErrors			///< use poisson errors for fits?
+// );
 
 /** pmSourceFitModel()
@@ -38,40 +54,39 @@
  */
 bool pmSourceFitModel(
-    pmSource *source,   ///< The input pmSource
-    pmModel *model,   ///< model to be fitted
-    pmSourceFitMode mode,  ///< define parameters to be fitted
+    pmSource *source,			///< The input pmSource
+    pmModel *model,			///< model to be fitted
+    pmSourceFitOptions *options,	///< define parameters to be fitted
     psImageMaskType maskVal		///< Value to mask
 );
 
-
-// initialize data for a group of object models
-bool pmSourceFitSetInit (pmModelType type);
-
-// clear data for a group of object models
-void pmSourceFitSetClear (void);
-
-// function used to set limits for a group of models
-bool pmSourceFitSet_CheckLimits (psMinConstraintMode mode, int nParam, float *params, float *betas);
-
-// function used to fit a group of object models
-psF32 pmSourceFitSet_Function(psVector *deriv,
-                              const psVector *params,
-                              const psVector *x);
-
-/** pmSourceFitSet()
- *
- * Fit the requested model to the specified source. The starting guess for the model is given
- * by the input source.model parameter values. The pixels of interest are specified by the
- * source.pixels and source.mask entries. This function calls psMinimizeLMChi2() on the image
- * data. The function returns TRUE on success or FALSE on failure.
- *
- */
-bool pmSourceFitSet(
-    pmSource *source,   ///< The input pmSource
-    psArray *modelSet,   ///< model to be fitted
-    pmSourceFitMode mode,  ///< define parameters to be fitted
-    psImageMaskType maskVal		///< Vale to mask
-
-);
+// // initialize data for a group of object models
+// bool pmSourceFitSetInit (pmModelType type);
+// 
+// // clear data for a group of object models
+// void pmSourceFitSetClear (void);
+// 
+// // function used to set limits for a group of models
+// bool pmSourceFitSet_CheckLimits (psMinConstraintMode mode, int nParam, float *params, float *betas);
+// 
+// // function used to fit a group of object models
+// psF32 pmSourceFitSet_Function(psVector *deriv,
+//                               const psVector *params,
+//                               const psVector *x);
+// 
+// /** pmSourceFitSet()
+//  *
+//  * Fit the requested model to the specified source. The starting guess for the model is given
+//  * by the input source.model parameter values. The pixels of interest are specified by the
+//  * source.pixels and source.mask entries. This function calls psMinimizeLMChi2() on the image
+//  * data. The function returns TRUE on success or FALSE on failure.
+//  *
+//  */
+// bool pmSourceFitSet(
+//     pmSource *source,   ///< The input pmSource
+//     psArray *modelSet,   ///< model to be fitted
+//     pmSourceFitMode mode,  ///< define parameters to be fitted
+//     psImageMaskType maskVal		///< Vale to mask
+// 
+// );
 
 /// @}
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceFitPCM.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceFitPCM.c	(revision 29060)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceFitPCM.c	(revision 29060)
@@ -0,0 +1,160 @@
+/* @file  pmSourceFitPCM.c
+ * structures and functions to support PSF-convolved model fitting
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.29 $
+ * @date $Date: 2009-02-16 22:30:50 $
+ * Copyright 2010 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <strings.h>
+#include <pslib.h>
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPAMaskWeight.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmSpan.h"
+#include "pmFootprintSpans.h"
+#include "pmFootprint.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
+#include "pmPCMdata.h"
+
+# define FACILITY "psModules.objects"
+
+// input source has both modelPSF and modelEXT.  on successful exit, we set the
+// modelConv to contain the fitted parameters, and the modelFlux to contain the 
+// convolved model image.
+
+bool pmSourceFitPCM (pmPCMdata *pcm, pmSource *source, pmSourceFitOptions *fitOptions, psImageMaskType maskVal, psImageMaskType markVal, int psfSize) {
+    
+    psVector *params  = pcm->modelConv->params;
+    psVector *dparams  = pcm->modelConv->dparams;
+
+    // force the floating parameters to fall within the contraint ranges
+    for (int i = 0; i < params->n; i++) {
+	pcm->modelConv->modelLimits (PS_MINIMIZE_PARAM_MIN, i, params->data.F32, NULL);
+	pcm->modelConv->modelLimits (PS_MINIMIZE_PARAM_MAX, i, params->data.F32, NULL);
+    }
+
+    // set up the minimization process
+    psMinimization *myMin = psMinimizationAlloc (fitOptions->nIter, fitOptions->minTol, fitOptions->maxTol);
+
+    psImage *covar = psImageAlloc (params->n, params->n, PS_TYPE_F32);
+
+    bool fitStatus = pmPCM_MinimizeChisq (myMin, covar, params, source, pcm);
+    for (int i = 0; i < dparams->n; i++) {
+        if ((pcm->constraint->paramMask != NULL) && pcm->constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[i])
+            continue;
+        dparams->data.F32[i] = sqrt(covar->data.F32[i][i]);
+        psTrace ("psModules.objects", 4, "%f +/- %f", params->data.F32[i], dparams->data.F32[i]);
+    }
+    psTrace ("psphot", 4, "niter: %d, chisq: %f", myMin->iter, myMin->value);
+
+    // renormalize output model image (generated by fitting process)
+    float Io = params->data.F32[PM_PAR_I0];
+    for (int iy = 0; iy < source->modelFlux->numRows; iy++) {
+	for (int ix = 0; ix < source->modelFlux->numCols; ix++) {
+	    source->modelFlux->data.F32[iy][ix] /= Io;
+	}
+    }
+
+    // save the resulting chisq, nDOF, nIter
+    pcm->modelConv->chisq = myMin->value;
+    pcm->modelConv->nIter = myMin->iter;
+    pcm->modelConv->nPix = pcm->nPix;
+    pcm->modelConv->nDOF = pcm->nDOF;
+    pcm->modelConv->chisqNorm = pcm->modelConv->chisq / pcm->modelConv->nDOF;
+    pcm->modelConv->flags |= PM_MODEL_STATUS_FITTED;
+    if (!fitStatus) pcm->modelConv->flags |= PM_MODEL_STATUS_NONCONVERGE;
+
+    // models can go insane: reject these
+    bool onPic = true;
+    onPic &= (params->data.F32[PM_PAR_XPOS] >= source->pixels->col0);
+    onPic &= (params->data.F32[PM_PAR_XPOS] <  source->pixels->col0 + source->pixels->numCols);
+    onPic &= (params->data.F32[PM_PAR_YPOS] >= source->pixels->row0);
+    onPic &= (params->data.F32[PM_PAR_YPOS] <  source->pixels->row0 + source->pixels->numRows);
+    if (!onPic) pcm->modelConv->flags |= PM_MODEL_STATUS_OFFIMAGE;
+
+    source->mode |= PM_SOURCE_MODE_FITTED; // XXX is this needed?
+
+    psFree(myMin);
+    psFree(covar);
+
+    return true;
+}
+
+bool pmSourceModelGuessPCM (pmPCMdata *pcm, pmSource *source, psImageMaskType maskVal, psImageMaskType markVal) {
+
+    if (!pcm->modelConv->modelGuess(pcm->modelConv, source)) {
+	return false;
+    }
+    return true;
+
+    // generate copy of the model
+    // XXX we could modify the parameter values or even the model 
+    // here based on the observed seeing (some lookup table...)
+
+    // XXX test : modify the Io, SXX, SYY terms based on the psf SXX, SYY terms:
+    // SXX,SYY in model parameters are sqrt(2) * shape.Sxx,Syy
+    psEllipseShape psfShape;
+    psfShape.sx  = source->modelPSF->params->data.F32[PM_PAR_SXX] / M_SQRT2;
+    psfShape.sxy = source->modelPSF->params->data.F32[PM_PAR_SXY];
+    psfShape.sy  = source->modelPSF->params->data.F32[PM_PAR_SYY] / M_SQRT2;
+
+    psEllipseAxes psfAxes = psEllipseShapeToAxes (psfShape, 20.0);
+    if (!isfinite(psfAxes.major)) return false;
+    if (!isfinite(psfAxes.minor)) return false;
+    if (!isfinite(psfAxes.theta)) return false;
+
+    // XXX test : modify the Io, SXX, SYY terms based on the psf SXX, SYY terms:
+    psEllipseShape extShape;
+    extShape.sx  = pcm->modelConv->params->data.F32[PM_PAR_SXX] / M_SQRT2;
+    extShape.sxy = pcm->modelConv->params->data.F32[PM_PAR_SXY];
+    extShape.sy  = pcm->modelConv->params->data.F32[PM_PAR_SYY] / M_SQRT2;
+
+    psEllipseAxes extAxes = psEllipseShapeToAxes (extShape, 20.0);
+    if (!isfinite(extAxes.major)) return false;
+    if (!isfinite(extAxes.minor)) return false;
+    if (!isfinite(extAxes.theta)) return false;
+
+    // decrease the initial guess ellipse by psf_minor axis:
+    psEllipseAxes extAxesMod;
+    extAxesMod.major = sqrt (PS_MAX (0.25, PS_SQR(extAxes.major) - PS_SQR(psfAxes.minor)));
+    extAxesMod.minor = sqrt (PS_MAX (0.25, PS_SQR(extAxes.minor) - PS_SQR(psfAxes.minor)));
+    extAxesMod.theta = extAxes.theta;
+
+    psEllipseShape extShapeMod = psEllipseAxesToShape (extAxesMod);
+    if (!isfinite(extShapeMod.sx))  return false;
+    if (!isfinite(extShapeMod.sy))  return false;
+    if (!isfinite(extShapeMod.sxy)) return false;
+
+    pcm->modelConv->params->data.F32[PM_PAR_SXX] = extShapeMod.sx * M_SQRT2;
+    pcm->modelConv->params->data.F32[PM_PAR_SXY] = extShapeMod.sxy;
+    pcm->modelConv->params->data.F32[PM_PAR_SYY] = extShapeMod.sy * M_SQRT2;
+
+    // increase the initial guess central intensity by 2pi r^2:
+    pcm->modelConv->params->data.F32[PM_PAR_I0] *= (1.0 + PS_SQR(psfAxes.minor) / PS_SQR(extAxesMod.minor));
+
+    return true;
+}
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceFitSet.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceFitSet.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceFitSet.c	(revision 29060)
@@ -22,23 +22,30 @@
 #include "pmHDU.h"
 #include "pmFPA.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmGrowthCurve.h"
-#include "pmResiduals.h"
-#include "pmTrend2D.h"
-#include "pmPSF.h"
+#include "pmModelFuncs.h"
 #include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
 #include "pmSource.h"
-#include "pmModelClass.h"
+
 #include "pmSourceFitModel.h"
 #include "pmSourceFitSet.h"
 
 // save as static values so they may be set externally
-static psF32 PM_SOURCE_FIT_MODEL_NUM_ITERATIONS = 15;
-static psF32 PM_SOURCE_FIT_MODEL_TOLERANCE = 0.1;
-static psF32 PM_SOURCE_FIT_MODEL_WEIGHT = 1.0;
-static bool  PM_SOURCE_FIT_MODEL_PIX_WEIGHTS = true;
+// static psF32 PM_SOURCE_FIT_MODEL_NUM_ITERATIONS = 15;
+// static psF32 PM_SOURCE_FIT_MODEL_TOLERANCE = 0.1;
+// static psF32 PM_SOURCE_FIT_MODEL_WEIGHT = 1.0;
+// static bool  PM_SOURCE_FIT_MODEL_PIX_WEIGHTS = true;
 
 /********************* Source Model Set Functions ***************************/
@@ -429,5 +436,5 @@
 bool pmSourceFitSet (pmSource *source,
                      psArray *modelSet,
-                     pmSourceFitMode mode,
+		     pmSourceFitOptions *options,
                      psImageMaskType maskVal)
 {
@@ -478,11 +485,11 @@
             // as variance to avoid the bias from systematic errors here we would just use the
             // source sky variance
-            if (PM_SOURCE_FIT_MODEL_PIX_WEIGHTS) {
-                yErr->data.F32[nPix] = 1.0 / source->variance->data.F32[i][j];
-            } else {
-                yErr->data.F32[nPix] = 1.0 / PM_SOURCE_FIT_MODEL_WEIGHT;
-            }
-            nPix++;
-        }
+            if (options->poissonErrors) {
+		yErr->data.F32[nPix] = 1.0 / source->variance->data.F32[i][j];
+	    } else {
+		yErr->data.F32[nPix] = 1.0 / options->weight;
+	    }
+	    nPix++;
+	}
     }
     x->n = nPix;
@@ -490,49 +497,49 @@
     yErr->n = nPix;
 
-    // create the FitSet for this thread and set the initial parameter guesses
+// create the FitSet for this thread and set the initial parameter guesses
     pmSourceFitSetData *thisSet = pmSourceFitSetDataSet(modelSet);
 
-    // define param and deriv vectors for complete set of parameters
+// define param and deriv vectors for complete set of parameters
     psVector *params = psVectorAlloc (thisSet->nParamSet, PS_TYPE_F32);
 
-    // set the param and deriv vectors based on the curent values
+// set the param and deriv vectors based on the curent values
     pmSourceFitSetJoin (NULL, params, thisSet);
 
-    // create the minimization constraints
+// create the minimization constraints
     psMinConstraint *constraint = psMinConstraintAlloc();
     constraint->paramMask = psVectorAlloc (thisSet->nParamSet, PS_TYPE_VECTOR_MASK);
     constraint->checkLimits = pmSourceFitSetCheckLimits;
 
-    pmSourceFitSetMasks (constraint, thisSet, mode);
-
-    // force the floating parameters to fall within the contraint ranges
+    pmSourceFitSetMasks (constraint, thisSet, options->mode);
+
+// force the floating parameters to fall within the contraint ranges
     for (int i = 0; i < params->n; i++) {
-        pmSourceFitSetCheckLimits (PS_MINIMIZE_PARAM_MIN, i, params->data.F32, NULL);
-        pmSourceFitSetCheckLimits (PS_MINIMIZE_PARAM_MAX, i, params->data.F32, NULL);
+	pmSourceFitSetCheckLimits (PS_MINIMIZE_PARAM_MIN, i, params->data.F32, NULL);
+	pmSourceFitSetCheckLimits (PS_MINIMIZE_PARAM_MAX, i, params->data.F32, NULL);
     }
 
     if (psTraceGetLevel("psModules.objects") >= 5) {
-        for (int i = 0; i < params->n; i++) {
-            fprintf (stderr, "%d %f %d\n", i, params->data.F32[i], constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[i]);
-        }
+	for (int i = 0; i < params->n; i++) {
+	    fprintf (stderr, "%d %f %d\n", i, params->data.F32[i], constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[i]);
+	}
     }
 
     if (nPix <  thisSet->nParamSet + 1) {
-        psTrace (__func__, 4, "insufficient valid pixels\n");
-        psTrace("psModules.objects", 10, "---- %s() end : fail pixels ----\n", __func__);
-        for (int i = 0; i < modelSet->n; i++) {
-            pmModel *model = modelSet->data[i];
-            model->flags |= PM_MODEL_STATUS_BADARGS;
-        }
-        psFree (x);
-        psFree (y);
-        psFree (yErr);
-        psFree (params);
-        psFree(constraint);
-        pmSourceFitSetDataClear(); // frees thisSet and removes if from the array of fitSets
-        return(false);
-    }
-
-    psMinimization *myMin = psMinimizationAlloc (PM_SOURCE_FIT_MODEL_NUM_ITERATIONS, PM_SOURCE_FIT_MODEL_TOLERANCE);
+	psTrace (__func__, 4, "insufficient valid pixels\n");
+	psTrace("psModules.objects", 10, "---- %s() end : fail pixels ----\n", __func__);
+	for (int i = 0; i < modelSet->n; i++) {
+	    pmModel *model = modelSet->data[i];
+	    model->flags |= PM_MODEL_STATUS_BADARGS;
+	}
+	psFree (x);
+	psFree (y);
+	psFree (yErr);
+	psFree (params);
+	psFree(constraint);
+	pmSourceFitSetDataClear(); // frees thisSet and removes if from the array of fitSets
+	return(false);
+    }
+
+    psMinimization *myMin = psMinimizationAlloc (options->nIter, options->minTol, options->maxTol);
 
     psImage *covar = psImageAlloc (params->n, params->n, PS_TYPE_F32);
@@ -540,35 +547,35 @@
     fitStatus = psMinimizeLMChi2(myMin, covar, params, constraint, x, y, yErr, pmSourceFitSetFunction);
     if (!fitStatus) {
-        psTrace("psModules.objects", 4, "Failed to fit model (%ld components)\n", modelSet->n);
-    }
-
-    // parameter errors from the covariance matrix
+	psTrace("psModules.objects", 4, "Failed to fit model (%ld components)\n", modelSet->n);
+    }
+
+// parameter errors from the covariance matrix
     psVector *dparams = psVectorAlloc (thisSet->nParamSet, PS_TYPE_F32);
     for (int i = 0; i < dparams->n; i++) {
-        if ((constraint->paramMask != NULL) && constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[i])
-            continue;
-        dparams->data.F32[i] = sqrt(covar->data.F32[i][i]);
-    }
-
-    // get the Gauss-Newton distance for fixed model parameters
+	if ((constraint->paramMask != NULL) && constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[i])
+	    continue;
+	dparams->data.F32[i] = sqrt(covar->data.F32[i][i]);
+    }
+
+// get the Gauss-Newton distance for fixed model parameters
     if (constraint->paramMask != NULL) {
-        psVector *delta = psVectorAlloc (params->n, PS_TYPE_F32);
-        psVector *altmask = psVectorAlloc (params->n, PS_TYPE_VECTOR_MASK);
-        altmask->data.PS_TYPE_VECTOR_MASK_DATA[0] = 1;
-        for (int i = 1; i < dparams->n; i++) {
-            altmask->data.PS_TYPE_VECTOR_MASK_DATA[i] = (constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) ? 0 : 1;
-        }
-        psMinimizeGaussNewtonDelta(delta, params, altmask, x, y, yErr, pmSourceFitSetFunction);
-
-        for (int i = 0; i < dparams->n; i++) {
-            if (!constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[i])
-                continue;
-            // note that delta is the value *subtracted* from the parameter
-            // to get the new guess.  for dparams to represent the direction
-            // of motion, we need to take -delta
-            dparams->data.F32[i] = -delta->data.F32[i];
-        }
-        psFree (delta);
-        psFree (altmask);
+	psVector *delta = psVectorAlloc (params->n, PS_TYPE_F32);
+	psVector *altmask = psVectorAlloc (params->n, PS_TYPE_VECTOR_MASK);
+	altmask->data.PS_TYPE_VECTOR_MASK_DATA[0] = 1;
+	for (int i = 1; i < dparams->n; i++) {
+	    altmask->data.PS_TYPE_VECTOR_MASK_DATA[i] = (constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) ? 0 : 1;
+	}
+	psMinimizeGaussNewtonDelta(delta, params, altmask, x, y, yErr, pmSourceFitSetFunction);
+
+	for (int i = 0; i < dparams->n; i++) {
+	    if (!constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[i])
+		continue;
+	    // note that delta is the value *subtracted* from the parameter
+	    // to get the new guess.  for dparams to represent the direction
+	    // of motion, we need to take -delta
+	    dparams->data.F32[i] = -delta->data.F32[i];
+	}
+	psFree (delta);
+	psFree (altmask);
     }
 
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceFitSet.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceFitSet.h	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceFitSet.h	(revision 29060)
@@ -56,5 +56,5 @@
     pmSource *source,                   ///< The input pmSource
     psArray *modelSet,                  ///< model to be fitted
-    pmSourceFitMode mode,               ///< define parameters to be fitted
+    pmSourceFitOptions *options,	///< define options for fitting process
     psImageMaskType maskVal             ///< Vale to mask
 
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceGroup.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceGroup.h	(revision 28957)
+++ 	(revision )
@@ -1,44 +1,0 @@
-/* @file  pmSourceGroup.h
- *
- * @author EAM, IfA
- *
- * @version $Revision: $
- * @date $Date: 2009-02-16 22:30:50 $
- * Copyright 2009 Institute for Astronomy, University of Hawaii
- */
-
-# ifndef PM_SOURCE_GROUP_H
-# define PM_SOURCE_GROUP_H
-
-#include <pslib.h>
-#include "pmPeaks.h"
-#include "pmModel.h"
-#include "pmMoments.h"
-#include "pmSourceExtendedPars.h"
-
-/// @addtogroup Objects Object Detection / Analysis Functions
-/// @{
-
-#include <pmSourceMasks.h>
-
-/** pmSourceGroup data structure
- *
- *  A source group is a connected set of source measurements with a
- *  common connection.  Each source in the group represents the
- *  detection of some astronomical object on a single each.  The group
- *  represents the related collection of measurements.  The fits are
- *  coupled in some way.  For example, they may all have the same
- *  position, but independent fluxes.  Or, they may have a common set
- *  of positions and shape parameters.  Or the position in each image
- *  may be related by a function.
- *
- *  XXX is thre any info that is neaded for each source group beyond that carried by the sources (besides ID)?
- */
-struct pmSource {
-  int seq;                            ///< ID for output (generated on write OR set on read)
-  psArray *sources;
-} pmSourceGroup;
-
-
-/// @}
-# endif /* PM_SOURCE_GROUP_H */
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceGroups.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceGroups.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceGroups.c	(revision 29060)
@@ -6,6 +6,28 @@
 #include <pslib.h>
 
+#include "pmHDU.h"
 #include "pmFPA.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmSpan.h"
+#include "pmFootprintSpans.h"
+#include "pmFootprint.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
 #include "pmSource.h"
+#include "pmSourceFitModel.h"
+#include "pmPSF.h"
+#include "pmPSFtry.h"
+#include "pmDetections.h"
+
 #include "pmSourceGroups.h"
 
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceGroups.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceGroups.h	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceGroups.h	(revision 29060)
@@ -1,8 +1,4 @@
 #ifndef PM_SOURCE_GROUPS_H
 #define PM_SOURCE_GROUPS_H
-
-#include <pslib.h>
-
-#include "pmFPA.h"
 
 /// Groups of sources
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO.c	(revision 29060)
@@ -31,16 +31,25 @@
 #include "pmConceptsRead.h"
 
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmGrowthCurve.h"
-#include "pmResiduals.h"
-#include "pmTrend2D.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
+#include "pmPSFtry.h"
+
 #include "pmDetections.h"
-#include "pmSource.h"
-#include "pmModelClass.h"
 #include "pmDetEff.h"
 #include "pmSourceIO.h"
@@ -326,4 +335,15 @@
 }
 
+# define PM_SOURCES_WRITE(NAME,TYPE)					\
+    if (!strcmp (exttype, NAME)) {					\
+	status &= pmSourcesWrite_##TYPE(file->fits, readout, sources, file->header, outhead, dataname, recipe); \
+	if (xsrcname) {							\
+	    status &= pmSourcesWrite_##TYPE##_XSRC(file->fits, readout, sources, file->header, xsrcname, recipe); \
+	}								\
+	if (xfitname) {							\
+	    status &= pmSourcesWrite_##TYPE##_XFIT (file->fits, readout, sources, file->header, xfitname); \
+	}								\
+    }
+
 // write out all readout-level Objects files for this cell
 bool pmReadoutWriteObjects (pmReadout *readout, const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
@@ -360,4 +380,5 @@
     }
 
+    // the older types (RAW, OBJ, SX, CMP) are for backwards compatibility -- deprecate eventually?
     switch (file->type) {
       case PM_FPA_FILE_RAW:
@@ -518,72 +539,19 @@
 		psMetadataAddStr (outhead, PS_LIST_TAIL, "XFITNAME", PS_META_REPLACE, "name of XFIT table extension", xfitname);
             }
-
-            // XXX these are case-sensitive since the EXTYPE is case-sensitive
+    
+
+            // these are case-sensitive since the EXTYPE is case-sensitive
             status = true;
-            if (!strcmp (exttype, "SMPDATA")) {
-                status &= pmSourcesWrite_SMPDATA (file->fits, sources, file->header, outhead, dataname);
-            }
-            if (!strcmp (exttype, "PS1_DEV_0")) {
-                status &= pmSourcesWrite_PS1_DEV_0 (file->fits, sources, file->header, outhead, dataname);
-            }
-            if (!strcmp (exttype, "PS1_DEV_1")) {
-                status &= pmSourcesWrite_PS1_DEV_1 (file->fits, sources, file->header, outhead, dataname);
-            }
-            if (!strcmp (exttype, "PS1_CAL_0")) {
-                status &= pmSourcesWrite_PS1_CAL_0 (file->fits, readout, sources, file->header, outhead, dataname);
-            }
-            if (!strcmp (exttype, "PS1_V1")) {
-                status &= pmSourcesWrite_CMF_PS1_V1 (file->fits, readout, sources, file->header, outhead, dataname);
-            }
-            if (!strcmp (exttype, "PS1_V2")) {
-                status &= pmSourcesWrite_CMF_PS1_V2 (file->fits, readout, sources, file->header, outhead, dataname);
-            }
-            if (!strcmp (exttype, "PS1_SV1")) {
-                status &= pmSourcesWrite_CMF_PS1_SV1 (file->fits, readout, sources, file->header, outhead, dataname, recipe);
-            }
-            if (!strcmp (exttype, "PS1_DV1")) {
-                status &= pmSourcesWrite_CMF_PS1_DV1 (file->fits, readout, sources, file->header, outhead, dataname);
-            }
-
-            if (xsrcname) {
-		if (!strcmp (exttype, "PS1_DEV_1")) {
-		    status &= pmSourcesWrite_PS1_DEV_1_XSRC (file->fits, sources, xsrcname, recipe);
-		}
-		if (!strcmp (exttype, "PS1_CAL_0")) {
-		    status &= pmSourcesWrite_PS1_CAL_0_XSRC (file->fits, readout, sources, file->header, xsrcname, recipe);
-		}
-		if (!strcmp (exttype, "PS1_V1")) {
-		    status &= pmSourcesWrite_CMF_PS1_V1_XSRC (file->fits, readout, sources, file->header, xsrcname, recipe);
-		}
-		if (!strcmp (exttype, "PS1_V2")) {
-		    status &= pmSourcesWrite_CMF_PS1_V2_XSRC (file->fits, readout, sources, file->header, xsrcname, recipe);
-		}
-		if (!strcmp (exttype, "PS1_SV1")) {
-		    status &= pmSourcesWrite_CMF_PS1_SV1_XSRC (file->fits, readout, sources, file->header, xsrcname, recipe);
-		}
-		if (!strcmp (exttype, "PS1_DV1")) {
-		    status &= pmSourcesWrite_CMF_PS1_DV1_XSRC (file->fits, readout, sources, file->header, xsrcname, recipe);
-		}
-            }
-            if (xfitname) {
-		if (!strcmp (exttype, "PS1_DEV_1")) {
-		    status &= pmSourcesWrite_PS1_DEV_1_XFIT (file->fits, sources, xfitname);
-		}
-		if (!strcmp (exttype, "PS1_CAL_0")) {
-		    status &= pmSourcesWrite_PS1_CAL_0_XFIT (file->fits, readout, sources, file->header, xfitname);
-		}
-		if (!strcmp (exttype, "PS1_V1")) {
-		    status &= pmSourcesWrite_CMF_PS1_V1_XFIT (file->fits, readout, sources, xfitname);
-		}
-		if (!strcmp (exttype, "PS1_V2")) {
-		    status &= pmSourcesWrite_CMF_PS1_V2_XFIT (file->fits, readout, sources, xfitname);
-		}
-		if (!strcmp (exttype, "PS1_SV1")) {
-		    status &= pmSourcesWrite_CMF_PS1_SV1_XFIT (file->fits, readout, sources, xfitname);
-		}
-		if (!strcmp (exttype, "PS1_DV1")) {
-		    status &= pmSourcesWrite_CMF_PS1_DV1_XFIT (file->fits, readout, sources, xfitname);
-		}
-            }
+	    PM_SOURCES_WRITE("SMPDATA",   SMPDATA);
+	    PM_SOURCES_WRITE("PS1_DEV_0", PS1_DEV_0);
+	    PM_SOURCES_WRITE("PS1_DEV_1", PS1_DEV_1);
+	    PM_SOURCES_WRITE("PS1_CAL_0", PS1_CAL_0);
+	    PM_SOURCES_WRITE("PS1_V1",    CMF_PS1_V1);
+	    PM_SOURCES_WRITE("PS1_V2",    CMF_PS1_V2);
+	    PM_SOURCES_WRITE("PS1_V3",    CMF_PS1_V3);
+	    PM_SOURCES_WRITE("PS1_SV1",   CMF_PS1_SV1);
+	    PM_SOURCES_WRITE("PS1_DV1",   CMF_PS1_DV1);
+	    PM_SOURCES_WRITE("PS1_DV2",   CMF_PS1_DV2);
+
 	    psFree (outhead);
 	    psFree (exttype);
@@ -1039,4 +1007,7 @@
                 sources = pmSourcesRead_CMF_PS1_DV1 (file->fits, hdu->header);
             }
+            if (!strcmp (exttype, "PS1_DV2")) {
+                sources = pmSourcesRead_CMF_PS1_DV2 (file->fits, hdu->header);
+            }
 
             if (!pmReadoutReadDetEff(file->fits, readout, deteffname)) {
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO.h	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO.h	(revision 29060)
@@ -24,32 +24,46 @@
 bool pmSourcesWriteCMP (psArray *sources, char *filename, psMetadata *header);
 
-bool pmSourcesWrite_SMPDATA (psFits *fits, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname);
-bool pmSourcesWrite_PS1_DEV_0 (psFits *fits, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname);
+bool pmSource_CMF_WritePHU (const pmFPAview *view, pmFPAfile *file, pmConfig *config);
 
-bool pmSourcesWrite_PS1_DEV_1 (psFits *fits, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname);
-bool pmSourcesWrite_PS1_DEV_1_XSRC (psFits *fits, psArray *sources, char *extname, psMetadata *recipe);
-bool pmSourcesWrite_PS1_DEV_1_XFIT (psFits *fits, psArray *sources, char *extname);
+// All of these functions need to use the same API, even if not all elements are used in a specific case
+bool pmSourcesWrite_SMPDATA(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe);
+bool pmSourcesWrite_SMPDATA_XSRC(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe);
+bool pmSourcesWrite_SMPDATA_XFIT(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname);
 
-bool pmSourcesWrite_PS1_CAL_0 (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname);
-bool pmSourcesWrite_PS1_CAL_0_XSRC (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe);
-bool pmSourcesWrite_PS1_CAL_0_XFIT (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname);
+bool pmSourcesWrite_PS1_DEV_0(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe);
+bool pmSourcesWrite_PS1_DEV_0_XSRC(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe);
+bool pmSourcesWrite_PS1_DEV_0_XFIT(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname);
 
-bool pmSourcesWrite_CMF_PS1_V1 (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname);
-bool pmSourcesWrite_CMF_PS1_V1_XSRC (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe);
-bool pmSourcesWrite_CMF_PS1_V1_XFIT (psFits *fits, pmReadout *readout, psArray *sources, char *extname);
+bool pmSourcesWrite_PS1_DEV_1(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe);
+bool pmSourcesWrite_PS1_DEV_1_XSRC(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe);
+bool pmSourcesWrite_PS1_DEV_1_XFIT(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname);
 
-bool pmSourcesWrite_CMF_PS1_V2 (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname);
-bool pmSourcesWrite_CMF_PS1_V2_XSRC (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe);
-bool pmSourcesWrite_CMF_PS1_V2_XFIT (psFits *fits, pmReadout *readout, psArray *sources, char *extname);
+bool pmSourcesWrite_PS1_CAL_0(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe);
+bool pmSourcesWrite_PS1_CAL_0_XSRC(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe);
+bool pmSourcesWrite_PS1_CAL_0_XFIT(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname);
 
-bool pmSourcesWrite_CMF_PS1_SV1 (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe);
-bool pmSourcesWrite_CMF_PS1_SV1_XSRC (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe);
-bool pmSourcesWrite_CMF_PS1_SV1_XFIT (psFits *fits, pmReadout *readout, psArray *sources, char *extname);
+bool pmSourcesWrite_CMF_PS1_V1(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe);
+bool pmSourcesWrite_CMF_PS1_V1_XSRC(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe);
+bool pmSourcesWrite_CMF_PS1_V1_XFIT(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname);
 
-bool pmSourcesWrite_CMF_PS1_DV1 (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname);
-bool pmSourcesWrite_CMF_PS1_DV1_XSRC (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe);
-bool pmSourcesWrite_CMF_PS1_DV1_XFIT (psFits *fits, pmReadout *readout, psArray *sources, char *extname);
+bool pmSourcesWrite_CMF_PS1_V2(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe);
+bool pmSourcesWrite_CMF_PS1_V2_XSRC(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe);
+bool pmSourcesWrite_CMF_PS1_V2_XFIT(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname);
 
-bool pmSource_CMF_WritePHU (const pmFPAview *view, pmFPAfile *file, pmConfig *config);
+bool pmSourcesWrite_CMF_PS1_V3(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe);
+bool pmSourcesWrite_CMF_PS1_V3_XSRC(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe);
+bool pmSourcesWrite_CMF_PS1_V3_XFIT(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname);
+
+bool pmSourcesWrite_CMF_PS1_SV1(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe);
+bool pmSourcesWrite_CMF_PS1_SV1_XSRC(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe);
+bool pmSourcesWrite_CMF_PS1_SV1_XFIT(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname);
+
+bool pmSourcesWrite_CMF_PS1_DV1(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe);
+bool pmSourcesWrite_CMF_PS1_DV1_XSRC(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe);
+bool pmSourcesWrite_CMF_PS1_DV1_XFIT(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname);
+
+bool pmSourcesWrite_CMF_PS1_DV2(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe);
+bool pmSourcesWrite_CMF_PS1_DV2_XSRC(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe);
+bool pmSourcesWrite_CMF_PS1_DV2_XFIT(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname);
 
 psArray *pmSourcesReadCMP (char *filename, psMetadata *header);
@@ -63,4 +77,5 @@
 psArray *pmSourcesRead_CMF_PS1_SV1 (psFits *fits, psMetadata *header);
 psArray *pmSourcesRead_CMF_PS1_DV1 (psFits *fits, psMetadata *header);
+psArray *pmSourcesRead_CMF_PS1_DV2 (psFits *fits, psMetadata *header);
 
 bool pmSourcesWritePSFs (psArray *sources, char *filename);
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_CMF_PS1_DV1.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_CMF_PS1_DV1.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_CMF_PS1_DV1.c	(revision 29060)
@@ -28,15 +28,24 @@
 #include "pmFPAfile.h"
 
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmGrowthCurve.h"
-#include "pmResiduals.h"
-#include "pmTrend2D.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
-#include "pmSource.h"
-#include "pmModelClass.h"
+#include "pmPSFtry.h"
+
 #include "pmSourceIO.h"
 
@@ -47,6 +56,5 @@
 // This version has elements intended for difference images & forced photometry:
 // diffStats entries (good for dipoles); flux + flux error (for insignificant detections)
-bool pmSourcesWrite_CMF_PS1_DV1 (psFits *fits, pmReadout *readout, psArray *sources,
-                                psMetadata *imageHeader, psMetadata *tableHeader, char *extname)
+bool pmSourcesWrite_CMF_PS1_DV1 (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe)
 {
     PS_ASSERT_PTR_NON_NULL(fits, false);
@@ -194,5 +202,5 @@
         psMetadataAdd (row, PS_LIST_TAIL, "PSF_MINOR",        PS_DATA_F32, "PSF width (minor axis)",                     axes.minor);
         psMetadataAdd (row, PS_LIST_TAIL, "PSF_THETA",        PS_DATA_F32, "PSF orientation angle",                      axes.theta);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF",           PS_DATA_F32, "PSF coverage/quality factor",                source->pixWeight);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF",           PS_DATA_F32, "PSF coverage/quality factor",                source->pixWeightNotBad);
         psMetadataAdd (row, PS_LIST_TAIL, "PSF_NDOF",         PS_DATA_S32, "degrees of freedom",                         nDOF);
         psMetadataAdd (row, PS_LIST_TAIL, "PSF_NPIX",         PS_DATA_S32, "number of pixels in fit",                    nPix);
@@ -324,9 +332,15 @@
         source->peak = pmPeakAlloc(PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], peakFlux, PM_PEAK_LONE);
         source->peak->flux = peakFlux;
+        source->peak->xf   = PAR[PM_PAR_XPOS]; // more accurate position
+        source->peak->yf   = PAR[PM_PAR_YPOS]; // more accurate position
         source->peak->dx   = dPAR[PM_PAR_XPOS];
         source->peak->dy   = dPAR[PM_PAR_YPOS];
-        source->peak->SN   = sqrt(source->peak->flux); // XXX a proxy: various functions sort by peak S/N
-
-        source->pixWeight = psMetadataLookupF32 (&status, row, "PSF_QF");
+        if (isfinite (source->errMag) && (source->errMag > 0.0)) {
+          source->peak->SN = 1.0 / source->errMag;
+        } else {
+          source->peak->SN = sqrt(source->peak->flux); // an alternate proxy: various functions sort by peak S/N
+        }
+
+        source->pixWeightNotBad = psMetadataLookupF32 (&status, row, "PSF_QF");
         source->crNsigma  = psMetadataLookupF32 (&status, row, "CR_NSIGMA");
         source->extNsigma = psMetadataLookupF32 (&status, row, "EXT_NSIGMA");
@@ -536,5 +550,5 @@
 
 // XXX this layout is still the same as PS1_DEV_1
-bool pmSourcesWrite_CMF_PS1_DV1_XFIT (psFits *fits, pmReadout *readout, psArray *sources, char *extname)
+bool pmSourcesWrite_CMF_PS1_DV1_XFIT(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname)
 {
 
@@ -585,4 +599,7 @@
             assert (model);
 
+	    // skip models which were not actually fitted
+	    if (model->flags & PM_MODEL_STATUS_BADARGS) continue;
+
             PAR = model->params->data.F32;
             dPAR = model->dparams->data.F32;
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_CMF_PS1_DV2.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_CMF_PS1_DV2.c	(revision 29060)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_CMF_PS1_DV2.c	(revision 29060)
@@ -0,0 +1,691 @@
+/** @file  pmSourceIO.c
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2009-02-18 02:44:19 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmDetrendDB.h"
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmFPAfile.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmSpan.h"
+#include "pmFootprintSpans.h"
+#include "pmFootprint.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
+#include "pmPSF.h"
+#include "pmPSFtry.h"
+
+#include "pmSourceIO.h"
+
+// panstarrs-style FITS table output (header + table in 1st extension)
+// this format consists of a header derived from the image header
+// followed by a zero-size matrix, followed by the table data
+
+// This version has elements intended for difference images & forced photometry:
+// diffStats entries (good for dipoles); flux + flux error (for insignificant detections)
+bool pmSourcesWrite_CMF_PS1_DV2 (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe)
+{
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_PTR_NON_NULL(sources, false);
+    PS_ASSERT_PTR_NON_NULL(extname, false);
+
+    int i;
+    psArray *table;
+    psMetadata *row;
+    psF32 *PAR, *dPAR;
+    psEllipseAxes axes;
+    psF32 xPos, yPos;
+    psF32 xErr, yErr;
+    psF32 errMag, chisq, apRadius;
+    psS32 nPix, nDOF;
+
+    pmChip *chip = readout->parent->parent;
+    pmFPA  *fpa  = chip->parent;
+
+    bool status1 = false;
+    bool status2 = false;
+    float magOffset = NAN;
+    float exptime   = psMetadataLookupF32 (&status1, fpa->concepts, "FPA.EXPOSURE");
+    float zeropt    = psMetadataLookupF32(&status2, fpa->concepts, "FPA.ZP");
+    if (!isfinite(zeropt)) {
+        zeropt    = psMetadataLookupF32 (&status2, imageHeader, "ZPT_OBS");
+    }
+    if (status1 && status2 && (exptime > 0.0)) {
+        magOffset = zeropt + 2.5*log10(exptime);
+    }
+    float zeroptErr = psMetadataLookupF32 (&status2, imageHeader, "ZPT_ERR");
+
+    // if the sequence is defined, write these in seq order; otherwise
+    // write them in S/N order:
+    if (sources->n > 0) {
+        pmSource *source = (pmSource *) sources->data[0];
+        if (source->seq == -1) {
+          // let's write these out in S/N order
+          sources = psArraySort (sources, pmSourceSortBySN);
+        } else {
+          sources = psArraySort (sources, pmSourceSortBySeq);
+        }
+    }
+
+    table = psArrayAllocEmpty (sources->n);
+
+    // we write out PSF-fits for all sources, regardless of quality.  the source flags tell us the state
+    // by the time we call this function, all values should be assigned.  let's use asserts to be sure in some cases.
+    for (i = 0; i < sources->n; i++) {
+        pmSource *source = (pmSource *) sources->data[i];
+
+        // If source->seq is -1, source was generated in this analysis.  If source->seq is
+        // not -1, source was read from elsewhere: in the latter case, preserve the source
+        // ID.  source.seq is used instead of source.id since the latter is a const
+        // generated on Alloc, and would thus be wrong for read in sources.
+        if (source->seq == -1) {
+          source->seq = i;
+        }
+
+        // no difference between PSF and non-PSF model
+        pmModel *model = source->modelPSF;
+
+        if (model != NULL) {
+            PAR = model->params->data.F32;
+            dPAR = model->dparams->data.F32;
+            xPos = PAR[PM_PAR_XPOS];
+            yPos = PAR[PM_PAR_YPOS];
+            if (source->mode & PM_SOURCE_MODE_NONLINEAR_FIT) {
+              xErr = dPAR[PM_PAR_XPOS];
+              yErr = dPAR[PM_PAR_YPOS];
+            } else {
+              // in linear-fit mode, there is no error on the centroid
+              xErr = source->peak->dx;
+              yErr = source->peak->dy;
+            }
+            if (isfinite(PAR[PM_PAR_SXX]) && isfinite(PAR[PM_PAR_SXX]) && isfinite(PAR[PM_PAR_SXX])) {
+                axes = pmPSF_ModelToAxes (PAR, 20.0);
+            } else {
+                axes.major = NAN;
+                axes.minor = NAN;
+                axes.theta = NAN;
+            }
+            chisq = model->chisq;
+            nDOF = model->nDOF;
+            nPix = model->nPix;
+            apRadius = source->apRadius;
+            errMag = model->dparams->data.F32[PM_PAR_I0] / model->params->data.F32[PM_PAR_I0];
+        } else {
+            xPos = source->peak->xf;
+            yPos = source->peak->yf;
+            xErr = source->peak->dx;
+            yErr = source->peak->dy;
+            axes.major = NAN;
+            axes.minor = NAN;
+            axes.theta = NAN;
+            chisq = NAN;
+            nDOF = 0;
+            nPix = 0;
+            apRadius = NAN;
+            errMag = NAN;
+        }
+
+        float calMag = isfinite(magOffset) ? source->psfMag + magOffset : NAN;
+        float peakMag = (source->peak->flux > 0) ? -2.5*log10(source->peak->flux) : NAN;
+        psS16 nImageOverlap = 1;
+
+        psSphere ptSky = {0.0, 0.0, 0.0, 0.0};
+        float posAngle = 0.0;
+        float pltScale = 0.0;
+        pmSourceLocalAstrometry (&ptSky, &posAngle, &pltScale, chip, xPos, yPos);
+
+	pmSourceDiffStats diffStats;
+	pmSourceDiffStatsInit(&diffStats);
+	if (source->diffStats) {
+	    diffStats = *source->diffStats;
+	}
+
+        row = psMetadataAlloc ();
+        psMetadataAdd (row, PS_LIST_TAIL, "IPP_IDET",         PS_DATA_U32, "IPP detection identifier index",             source->seq);
+        psMetadataAdd (row, PS_LIST_TAIL, "X_PSF",            PS_DATA_F32, "PSF x coordinate",                           xPos);
+        psMetadataAdd (row, PS_LIST_TAIL, "Y_PSF",            PS_DATA_F32, "PSF y coordinate",                           yPos);
+        psMetadataAdd (row, PS_LIST_TAIL, "X_PSF_SIG",        PS_DATA_F32, "Sigma in PSF x coordinate",                  xErr); // XXX this is only measured for non-linear fits
+        psMetadataAdd (row, PS_LIST_TAIL, "Y_PSF_SIG",        PS_DATA_F32, "Sigma in PSF y coordinate",                  yErr); // XXX this is only measured for non-linear fits
+        psMetadataAdd (row, PS_LIST_TAIL, "POSANGLE",         PS_DATA_F32, "position angle at source (degrees)",         posAngle*PS_DEG_RAD);
+        psMetadataAdd (row, PS_LIST_TAIL, "PLTSCALE",         PS_DATA_F32, "plate scale at source (arcsec/pixel)",       pltScale*PS_DEG_RAD*3600.0);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_MAG",     PS_DATA_F32, "PSF fit instrumental magnitude",             source->psfMag);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_MAG_SIG", PS_DATA_F32, "Sigma of PSF instrumental magnitude",        errMag);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_FLUX",    PS_DATA_F32, "PSF fit instrumental magnitude",             source->psfFlux);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_FLUX_SIG",PS_DATA_F32, "Sigma of PSF instrumental magnitude",        source->psfFluxErr);
+        psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG",           PS_DATA_F32, "magnitude in standard aperture",             source->apMag);
+        psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG_RADIUS",    PS_DATA_F32, "radius used for aperture mags",              apRadius);
+        psMetadataAdd (row, PS_LIST_TAIL, "PEAK_FLUX_AS_MAG", PS_DATA_F32, "Peak flux expressed as magnitude",           peakMag);
+        psMetadataAdd (row, PS_LIST_TAIL, "CAL_PSF_MAG",      PS_DATA_F32, "PSF Magnitude using supplied calibration",   calMag);
+        psMetadataAdd (row, PS_LIST_TAIL, "CAL_PSF_MAG_SIG",  PS_DATA_F32, "measured scatter of zero point calibration", zeroptErr);
+        psMetadataAdd (row, PS_LIST_TAIL, "RA_PSF",           PS_DATA_F64, "PSF RA coordinate (degrees)",                ptSky.r*PS_DEG_RAD);
+        psMetadataAdd (row, PS_LIST_TAIL, "DEC_PSF",          PS_DATA_F64, "PSF DEC coordinate (degrees)",               ptSky.d*PS_DEG_RAD);
+        psMetadataAdd (row, PS_LIST_TAIL, "SKY",              PS_DATA_F32, "Sky level",                                  source->sky);
+        psMetadataAdd (row, PS_LIST_TAIL, "SKY_SIGMA",        PS_DATA_F32, "Sigma of sky level",                         source->skyErr);
+
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_CHISQ",        PS_DATA_F32, "Chisq of PSF-fit",                           chisq);
+        psMetadataAdd (row, PS_LIST_TAIL, "CR_NSIGMA",        PS_DATA_F32, "Nsigma deviations from PSF to CF",           source->crNsigma);
+        psMetadataAdd (row, PS_LIST_TAIL, "EXT_NSIGMA",       PS_DATA_F32, "Nsigma deviations from PSF to EXT",          source->extNsigma);
+
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_MAJOR",        PS_DATA_F32, "PSF width (major axis)",                     axes.major);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_MINOR",        PS_DATA_F32, "PSF width (minor axis)",                     axes.minor);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_THETA",        PS_DATA_F32, "PSF orientation angle",                      axes.theta);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF",           PS_DATA_F32, "PSF coverage/quality factor (bad)",          source->pixWeightNotBad);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF_PERFECT",   PS_DATA_F32, "PSF coverage/quality factor (poor)",         source->pixWeightNotPoor);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_NDOF",         PS_DATA_S32, "degrees of freedom",                         nDOF);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_NPIX",         PS_DATA_S32, "number of pixels in fit",                    nPix);
+
+        // distinguish moments measure from window vs S/N > XX ??
+        float mxx = source->moments ? source->moments->Mxx : NAN;
+        float mxy = source->moments ? source->moments->Mxy : NAN;
+        float myy = source->moments ? source->moments->Myy : NAN;
+        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_XX",       PS_DATA_F32, "second moments (X^2)",                      mxx);
+        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_XY",       PS_DATA_F32, "second moments (X*Y)",                      mxy);
+        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_YY",       PS_DATA_F32, "second moments (Y*Y)",                      myy);
+
+        psMetadataAdd (row, PS_LIST_TAIL, "DIFF_NPOS",        PS_DATA_S32, "nPos (n pix > 3 sigma)",                    diffStats.nGood);
+        psMetadataAdd (row, PS_LIST_TAIL, "DIFF_FRATIO",      PS_DATA_F32, "fPos / (fPos + fNeg)",                      diffStats.fRatio);
+        psMetadataAdd (row, PS_LIST_TAIL, "DIFF_NRATIO_BAD",  PS_DATA_F32, "nPos / (nPos + nNeg)",                      diffStats.nRatioBad);
+        psMetadataAdd (row, PS_LIST_TAIL, "DIFF_NRATIO_MASK", PS_DATA_F32, "nPos / (nPos + nMask)",                     diffStats.nRatioMask);
+        psMetadataAdd (row, PS_LIST_TAIL, "DIFF_NRATIO_ALL",  PS_DATA_F32, "nPos / (nGood + nMask + nBad)",             diffStats.nRatioAll);
+
+        psMetadataAdd (row, PS_LIST_TAIL, "DIFF_R_P",         PS_DATA_F32, "distance to positive match source",          diffStats.Rp);
+        psMetadataAdd (row, PS_LIST_TAIL, "DIFF_SN_P",        PS_DATA_F32, "signal-to-noise of pos match src",           diffStats.SNp);
+        psMetadataAdd (row, PS_LIST_TAIL, "DIFF_R_M",         PS_DATA_F32, "distance to negative match source",          diffStats.Rm);
+        psMetadataAdd (row, PS_LIST_TAIL, "DIFF_SN_M",        PS_DATA_F32, "signal-to-noise of neg match src",           diffStats.SNm);
+
+        psMetadataAdd (row, PS_LIST_TAIL, "FLAGS",            PS_DATA_U32, "psphot analysis flags",                      source->mode);
+        psMetadataAdd (row, PS_LIST_TAIL, "FLAGS2",           PS_DATA_U32, "psphot analysis flags (group 2)",            source->mode2);
+
+        // XXX not sure how to get this : need to load Nimages with weight?
+        psMetadataAdd (row, PS_LIST_TAIL, "N_FRAMES",         PS_DATA_U16, "Number of frames overlapping source center", nImageOverlap);
+        psMetadataAdd (row, PS_LIST_TAIL, "PADDING",          PS_DATA_S16, "padding", 0);
+
+        psArrayAdd (table, 100, row);
+        psFree (row);
+
+        // EXT_NSIGMA will be NAN if: 1) contour ellipse is imaginary; 2) source is not
+        // subtracted
+
+        // CR_NSIGMA will be NAN if: 1) source is not subtracted; 2) source is on the image
+        // edge; 3) any pixels in the 3x3 peak region are masked;
+    }
+
+    psMetadata *header = psMetadataCopy(NULL, tableHeader);
+    pmSourceMasksHeader(header);
+
+    if (table->n == 0) {
+        psFitsWriteBlank(fits, header, extname);
+        psFree(table);
+        psFree(header);
+        return true;
+    }
+
+    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
+    if (!psFitsWriteTable(fits, header, table, extname)) {
+        psError(PS_ERR_IO, false, "writing ext data %s\n", extname);
+        psFree(table);
+        psFree(header);
+        return false;
+    }
+    psFree(table);
+    psFree(header);
+
+    return true;
+}
+
+// read in a readout from the fits file
+psArray *pmSourcesRead_CMF_PS1_DV2 (psFits *fits, psMetadata *header)
+{
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_PTR_NON_NULL(header, false);
+
+    bool status;
+    psF32 *PAR, *dPAR;
+    psEllipseAxes axes;
+
+    // define PSF model type
+    // XXX need to carry the extra model parameters
+    int modelType = pmModelClassGetType ("PS_MODEL_GAUSS");
+
+    char *PSF_NAME = psMetadataLookupStr (&status, header, "PSF_NAME");
+    if (PSF_NAME != NULL) {
+        modelType = pmModelClassGetType (PSF_NAME);
+    }
+    assert (modelType > -1);
+
+    // We get the size of the table, and allocate the array of sources first because the table
+    // is large and ephemeral --- when the table gets blown away, whatever is allocated after
+    // the table is read blocks the free.  In fact, it's better to read the table row by row.
+    long numSources = psFitsTableSize(fits); // Number of sources in table
+    psArray *sources = psArrayAlloc(numSources); // Array of sources, to return
+
+    // convert the table to the pmSource entriesa
+    for (int i = 0; i < numSources; i++) {
+        psMetadata *row = psFitsReadTableRow(fits, i); // Table row
+
+        pmSource *source = pmSourceAlloc ();
+        pmModel *model = pmModelAlloc (modelType);
+        source->modelPSF  = model;
+        source->type = PM_SOURCE_TYPE_STAR; // XXX this should be added to the flags
+
+        // NOTE: A SEGV here because "model" is NULL is probably caused by not initialising the models.
+        PAR = model->params->data.F32;
+        dPAR = model->dparams->data.F32;
+
+        source->seq       = psMetadataLookupU32 (&status, row, "IPP_IDET");
+        PAR[PM_PAR_XPOS]  = psMetadataLookupF32 (&status, row, "X_PSF");
+        PAR[PM_PAR_YPOS]  = psMetadataLookupF32 (&status, row, "Y_PSF");
+        dPAR[PM_PAR_XPOS] = psMetadataLookupF32 (&status, row, "X_PSF_SIG");
+        dPAR[PM_PAR_YPOS] = psMetadataLookupF32 (&status, row, "Y_PSF_SIG");
+        axes.major        = psMetadataLookupF32 (&status, row, "PSF_MAJOR");
+        axes.minor        = psMetadataLookupF32 (&status, row, "PSF_MINOR");
+        axes.theta        = psMetadataLookupF32 (&status, row, "PSF_THETA");
+
+        PAR[PM_PAR_SKY]   = psMetadataLookupF32 (&status, row, "SKY");
+        dPAR[PM_PAR_SKY]  = psMetadataLookupF32 (&status, row, "SKY_SIGMA");
+        source->sky       = PAR[PM_PAR_SKY];
+        source->skyErr    = dPAR[PM_PAR_SKY];
+
+        // XXX use these to determine PAR[PM_PAR_I0]?
+        source->psfFlux    = psMetadataLookupF32 (&status, row, "PSF_INST_FLUX");
+        source->psfFluxErr = psMetadataLookupF32 (&status, row, "PSF_INST_FLUX_SIG");
+
+        source->psfMag    = psMetadataLookupF32 (&status, row, "PSF_INST_MAG");
+        source->errMag    = psMetadataLookupF32 (&status, row, "PSF_INST_MAG_SIG");
+        source->apMag     = psMetadataLookupF32 (&status, row, "AP_MAG");
+
+        // XXX this scaling is incorrect: does not include the 2 \pi AREA factor
+        PAR[PM_PAR_I0]    = (isfinite(source->psfMag)) ? pow(10.0, -0.4*source->psfMag) : NAN;
+        dPAR[PM_PAR_I0]   = (isfinite(source->psfMag)) ? PAR[PM_PAR_I0] * source->errMag : NAN;
+
+        pmPSF_AxesToModel (PAR, axes);
+
+        float peakMag     = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
+        float peakFlux    = (isfinite(peakMag)) ? pow(10.0, -0.4*peakMag) : NAN;
+
+        // recreate the peak to match (xPos, yPos) +/- (xErr, yErr)
+        source->peak = pmPeakAlloc(PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], peakFlux, PM_PEAK_LONE);
+        source->peak->flux = peakFlux;
+        source->peak->xf   = PAR[PM_PAR_XPOS]; // more accurate position
+        source->peak->yf   = PAR[PM_PAR_YPOS]; // more accurate position
+        source->peak->dx   = dPAR[PM_PAR_XPOS];
+        source->peak->dy   = dPAR[PM_PAR_YPOS];
+        if (isfinite (source->errMag) && (source->errMag > 0.0)) {
+          source->peak->SN = 1.0 / source->errMag;
+        } else {
+          source->peak->SN = sqrt(source->peak->flux); // an alternate proxy: various functions sort by peak S/N
+        }
+
+        source->pixWeightNotBad = psMetadataLookupF32 (&status, row, "PSF_QF");
+        source->pixWeightNotPoor = psMetadataLookupF32 (&status, row, "PSF_QF_PERFECT");
+        source->crNsigma  = psMetadataLookupF32 (&status, row, "CR_NSIGMA");
+        source->extNsigma = psMetadataLookupF32 (&status, row, "EXT_NSIGMA");
+        source->apRadius  = psMetadataLookupS32 (&status, row, "AP_MAG_RADIUS");
+
+        // note that some older versions used PSF_PROBABILITY: this was not well defined.
+        model->chisq      = psMetadataLookupF32 (&status, row, "PSF_CHISQ");
+        model->nDOF       = psMetadataLookupS32 (&status, row, "PSF_NDOF");
+        model->nPix       = psMetadataLookupS32 (&status, row, "PSF_NPIX");
+
+        source->moments = pmMomentsAlloc ();
+        source->moments->Mxx = psMetadataLookupF32 (&status, row, "MOMENTS_XX");
+        source->moments->Mxy = psMetadataLookupF32 (&status, row, "MOMENTS_XY");
+        source->moments->Myy = psMetadataLookupF32 (&status, row, "MOMENTS_YY");
+
+        source->mode = psMetadataLookupU32 (&status, row, "FLAGS");
+        source->mode2 = psMetadataLookupU32 (&status, row, "FLAGS2");
+        assert (status);
+
+	int nPos = psMetadataLookupS32 (&status, row, "DIFF_NPOS");
+	if (nPos) {
+	    source->diffStats = pmSourceDiffStatsAlloc();
+	    source->diffStats->nGood      = nPos;
+	    source->diffStats->fRatio     = psMetadataLookupF32 (&status, row, "DIFF_FRATIO");
+	    source->diffStats->nRatioBad  = psMetadataLookupF32 (&status, row, "DIFF_NRATIO_BAD");
+	    source->diffStats->nRatioMask = psMetadataLookupF32 (&status, row, "DIFF_NRATIO_MASK");
+	    source->diffStats->nRatioAll  = psMetadataLookupF32 (&status, row, "DIFF_NRATIO_ALL");
+
+	    source->diffStats->Rp         = psMetadataLookupF32 (&status, row, "DIFF_R_P");
+	    source->diffStats->SNp        = psMetadataLookupF32 (&status, row, "DIFF_SN_P");
+	    source->diffStats->Rm         = psMetadataLookupF32 (&status, row, "DIFF_R_M");
+	    source->diffStats->SNm        = psMetadataLookupF32 (&status, row, "DIFF_SN_M");
+	}
+
+        sources->data[i] = source;
+        psFree(row);
+    }
+
+    return sources;
+}
+
+// XXX this layout is still the same as PS1_DEV_1
+bool pmSourcesWrite_CMF_PS1_DV2_XSRC (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe)
+{
+
+    bool status;
+    psArray *table;
+    psMetadata *row;
+    psF32 *PAR, *dPAR;
+    psF32 xPos, yPos;
+    psF32 xErr, yErr;
+
+    // create a header to hold the output data
+    psMetadata *outhead = psMetadataAlloc ();
+
+    // write the links to the image header
+    psMetadataAddStr (outhead, PS_LIST_TAIL, "EXTNAME", PS_META_REPLACE, "xsrc table extension", extname);
+
+    // let's write these out in S/N order
+    sources = psArraySort (sources, pmSourceSortBySN);
+
+    table = psArrayAllocEmpty (sources->n);
+
+    // which extended source analyses should we perform?
+    // bool doPetrosian    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_PETROSIAN");
+    // bool doIsophotal    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ISOPHOTAL");
+    // bool doAnnuli       = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ANNULI");
+    // bool doKron         = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_KRON");
+
+    psVector *radialBinsLower = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.LOWER");
+    psVector *radialBinsUpper = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.UPPER");
+    assert (radialBinsLower->n == radialBinsUpper->n);
+
+    // we write out all sources, regardless of quality.  the source flags tell us the state
+    for (int i = 0; i < sources->n; i++) {
+        // skip source if it is not a ext sourc
+        // XXX we have two places that extended source parameters are measured:
+        // psphotExtendedSources, which measures the aperture-like parameters and (potentially) the psf-convolved extended source models,
+        // psphotFitEXT, which does the simple extended source model fit (not psf-convolved)
+        // should we require both?
+
+        pmSource *source = sources->data[i];
+
+        // skip sources without measurements
+        if (source->extpars == NULL) continue;
+
+        // we require a PSF model fit (ignore the real crud)
+        pmModel *model = source->modelPSF;
+        if (model == NULL) continue;
+
+        // XXX I need to split the extended models from the extended aperture measurements
+        PAR = model->params->data.F32;
+        dPAR = model->dparams->data.F32;
+        xPos = PAR[PM_PAR_XPOS];
+        yPos = PAR[PM_PAR_YPOS];
+        xErr = dPAR[PM_PAR_XPOS];
+        yErr = dPAR[PM_PAR_YPOS];
+
+        row = psMetadataAlloc ();
+
+        // XXX we are not writing out the mode (flags) or the type (psf, ext, etc)
+        psMetadataAdd (row, PS_LIST_TAIL, "IPP_IDET",         PS_DATA_U32, "IPP detection identifier index",             source->seq);
+        psMetadataAdd (row, PS_LIST_TAIL, "X_EXT",            PS_DATA_F32, "EXT model x coordinate",                     xPos);
+        psMetadataAdd (row, PS_LIST_TAIL, "Y_EXT",            PS_DATA_F32, "EXT model y coordinate",                     yPos);
+        psMetadataAdd (row, PS_LIST_TAIL, "X_EXT_SIG",        PS_DATA_F32, "Sigma in EXT x coordinate",                  xErr);
+        psMetadataAdd (row, PS_LIST_TAIL, "Y_EXT_SIG",        PS_DATA_F32, "Sigma in EXT y coordinate",                  yErr);
+
+# if (0)
+        // Petrosian measurements
+        // XXX insert header data: petrosian ref radius, flux ratio
+        if (doPetrosian) {
+            pmSourcePetrosianValues *petrosian = source->extpars->petrosian;
+            if (petrosian) {
+                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG",        PS_DATA_F32, "Petrosian Magnitude",       petrosian->mag);
+                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG_ERR",    PS_DATA_F32, "Petrosian Magnitude Error", petrosian->magErr);
+                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS",     PS_DATA_F32, "Petrosian Radius",          petrosian->rad);
+                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_ERR", PS_DATA_F32, "Petrosian Radius Error",    petrosian->radErr);
+            } else {
+                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG",        PS_DATA_F32, "Petrosian Magnitude",       NAN);
+                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG_ERR",    PS_DATA_F32, "Petrosian Magnitude Error", NAN);
+                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS",     PS_DATA_F32, "Petrosian Radius",          NAN);
+                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_ERR", PS_DATA_F32, "Petrosian Radius Error",    NAN);
+            }
+        }
+
+        // Kron measurements
+        if (doKron) {
+            pmSourceKronValues *kron = source->extpars->kron;
+            if (kron) {
+                psMetadataAdd (row, PS_LIST_TAIL, "KRON_MAG",        PS_DATA_F32, "Kron Magnitude",       kron->mag);
+                psMetadataAdd (row, PS_LIST_TAIL, "KRON_MAG_ERR",    PS_DATA_F32, "Kron Magnitude Error", kron->magErr);
+                psMetadataAdd (row, PS_LIST_TAIL, "KRON_RADIUS",     PS_DATA_F32, "Kron Radius",          kron->rad);
+                psMetadataAdd (row, PS_LIST_TAIL, "KRON_RADIUS_ERR", PS_DATA_F32, "Kron Radius Error",    kron->radErr);
+            } else {
+                psMetadataAdd (row, PS_LIST_TAIL, "KRON_MAG",        PS_DATA_F32, "Kron Magnitude",       NAN);
+                psMetadataAdd (row, PS_LIST_TAIL, "KRON_MAG_ERR",    PS_DATA_F32, "Kron Magnitude Error", NAN);
+                psMetadataAdd (row, PS_LIST_TAIL, "KRON_RADIUS",     PS_DATA_F32, "Kron Radius",          NAN);
+                psMetadataAdd (row, PS_LIST_TAIL, "KRON_RADIUS_ERR", PS_DATA_F32, "Kron Radius Error",    NAN);
+            }
+        }
+
+        // Isophot measurements
+        // XXX insert header data: isophotal level
+        if (doIsophotal) {
+            pmSourceIsophotalValues *isophot = source->extpars->isophot;
+            if (isophot) {
+                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_MAG",        PS_DATA_F32, "Isophot Magnitude",       isophot->mag);
+                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_MAG_ERR",    PS_DATA_F32, "Isophot Magnitude Error", isophot->magErr);
+                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_RADIUS",     PS_DATA_F32, "Isophot Radius",          isophot->rad);
+                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_RADIUS_ERR", PS_DATA_F32, "Isophot Radius Error",    isophot->radErr);
+            } else {
+                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_MAG",        PS_DATA_F32, "Isophot Magnitude",       NAN);
+                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_MAG_ERR",    PS_DATA_F32, "Isophot Magnitude Error", NAN);
+                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_RADIUS",     PS_DATA_F32, "Isophot Radius",          NAN);
+                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_RADIUS_ERR", PS_DATA_F32, "Isophot Radius Error",    NAN);
+            }
+        }
+
+        // Flux Annuli
+        if (doAnnuli) {
+            pmSourceAnnuli *annuli = source->extpars->annuli;
+            if (annuli) {
+                psVector *fluxVal = annuli->flux;
+                psVector *fluxErr = annuli->fluxErr;
+                psVector *fluxVar = annuli->fluxVar;
+
+                for (int j = 0; j < fluxVal->n; j++) {
+                    char name[32];
+                    sprintf (name, "FLUX_VAL_R_%02d", j);
+                    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux value in annulus", fluxVal->data.F32[j]);
+                    sprintf (name, "FLUX_ERR_R_%02d", j);
+                    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux error in annulus", fluxErr->data.F32[j]);
+                    sprintf (name, "FLUX_VAR_R_%02d", j);
+                    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux stdev in annulus", fluxVar->data.F32[j]);
+                }
+            } else {
+                for (int j = 0; j < radialBinsLower->n; j++) {
+                    char name[32];
+                    sprintf (name, "FLUX_VAL_R_%02d", j);
+                    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux value in annulus", NAN);
+                    sprintf (name, "FLUX_ERR_R_%02d", j);
+                    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux error in annulus", NAN);
+                    sprintf (name, "FLUX_VAR_R_%02d", j);
+                    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux stdev in annulus", NAN);
+                }
+            }
+        }
+
+# endif
+        psArrayAdd (table, 100, row);
+        psFree (row);
+    }
+
+    if (table->n == 0) {
+        psFitsWriteBlank (fits, outhead, extname);
+        psFree (outhead);
+        psFree (table);
+        return true;
+    }
+
+    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
+    if (!psFitsWriteTable (fits, outhead, table, extname)) {
+        psError(PS_ERR_IO, false, "writing ext data %s\n", extname);
+        psFree (outhead);
+        psFree(table);
+        return false;
+    }
+    psFree (outhead);
+    psFree (table);
+
+    return true;
+}
+
+// XXX this layout is still the same as PS1_DEV_1
+bool pmSourcesWrite_CMF_PS1_DV2_XFIT(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname)
+{
+
+    psArray *table;
+    psMetadata *row;
+    psF32 *PAR, *dPAR;
+    psEllipseAxes axes;
+    psF32 xPos, yPos;
+    psF32 xErr, yErr;
+    char name[64];
+
+    // create a header to hold the output data
+    psMetadata *outhead = psMetadataAlloc ();
+
+    // write the links to the image header
+    psMetadataAddStr (outhead, PS_LIST_TAIL, "EXTNAME", PS_META_REPLACE, "xsrc table extension", extname);
+
+    // let's write these out in S/N order
+    sources = psArraySort (sources, pmSourceSortBySN);
+
+    // we are writing one row per model; we need to write out same number of columns for each row: find the max Nparams
+    int nParamMax = 0;
+    for (int i = 0; i < sources->n; i++) {
+        pmSource *source = sources->data[i];
+        if (source->modelFits == NULL) continue;
+        for (int j = 0; j < source->modelFits->n; j++) {
+            pmModel *model = source->modelFits->data[j];
+            assert (model);
+            nParamMax = PS_MAX (nParamMax, model->params->n);
+        }
+    }
+
+    table = psArrayAllocEmpty (sources->n);
+
+    // we write out all sources, regardless of quality.  the source flags tell us the state
+    for (int i = 0; i < sources->n; i++) {
+
+        pmSource *source = sources->data[i];
+
+        // XXX if no model fits are saved, write out modelEXT?
+        if (source->modelFits == NULL) continue;
+
+        // We have multiple sources : need to flag the one used to subtract the light (the 'best' model)
+        for (int j = 0; j < source->modelFits->n; j++) {
+
+            // choose the convolved EXT model, if available, otherwise the simple one
+            pmModel *model = source->modelFits->data[j];
+            assert (model);
+
+	    // skip models which were not actually fitted
+	    if (model->flags & PM_MODEL_STATUS_BADARGS) continue;
+
+            PAR = model->params->data.F32;
+            dPAR = model->dparams->data.F32;
+            xPos = PAR[PM_PAR_XPOS];
+            yPos = PAR[PM_PAR_YPOS];
+            xErr = dPAR[PM_PAR_XPOS];
+            yErr = dPAR[PM_PAR_YPOS];
+
+            axes = pmPSF_ModelToAxes (PAR, 20.0);
+
+            row = psMetadataAlloc ();
+
+            // XXX we are not writing out the mode (flags) or the type (psf, ext, etc)
+            psMetadataAddU32 (row, PS_LIST_TAIL, "IPP_IDET",         0, "IPP detection identifier index",             source->seq);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "X_EXT",            0, "EXT model x coordinate",                     xPos);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "Y_EXT",            0, "EXT model y coordinate",                     yPos);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "X_EXT_SIG",        0, "Sigma in EXT x coordinate",                  xErr);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "Y_EXT_SIG",        0, "Sigma in EXT y coordinate",                  yErr);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_INST_MAG",     0, "EXT fit instrumental magnitude",             model->mag);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_INST_MAG_SIG", 0, "Sigma of PSF instrumental magnitude",        model->magErr);
+
+            psMetadataAddF32 (row, PS_LIST_TAIL, "NPARAMS",          0, "number of model parameters",                 model->params->n);
+            psMetadataAddStr (row, PS_LIST_TAIL, "MODEL_TYPE",       0, "name of model",                              pmModelClassGetName (model->type));
+
+            // XXX these should be major and minor, not 'x' and 'y'
+            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_WIDTH_MAJ",    0, "EXT width in x coordinate",                  axes.major);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_WIDTH_MIN",    0, "EXT width in y coordinate",                  axes.minor);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_THETA",        0, "EXT orientation angle",                      axes.theta);
+
+            // write out the other generic parameters
+            for (int k = 0; k < nParamMax; k++) {
+                if (k == PM_PAR_I0) continue;
+                if (k == PM_PAR_SKY) continue;
+                if (k == PM_PAR_XPOS) continue;
+                if (k == PM_PAR_YPOS) continue;
+                if (k == PM_PAR_SXX) continue;
+                if (k == PM_PAR_SXY) continue;
+                if (k == PM_PAR_SYY) continue;
+
+                snprintf (name, 64, "EXT_PAR_%02d", k);
+
+                if (k < model->params->n) {
+                    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "", model->params->data.F32[k]);
+                } else {
+                    psMetadataAddF32 (row, PS_LIST_TAIL, name, PS_DATA_F32, "", NAN);
+                }
+            }
+
+            // XXX other parameters which may be set.
+            // XXX flag / value to define the model
+            // XXX write out the model type, fit status flags
+
+            psArrayAdd (table, 100, row);
+            psFree (row);
+        }
+    }
+
+    if (table->n == 0) {
+        psFitsWriteBlank (fits, outhead, extname);
+        psFree (outhead);
+        psFree (table);
+        return true;
+    }
+
+    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
+    if (!psFitsWriteTable (fits, outhead, table, extname)) {
+        psError(PS_ERR_IO, false, "writing ext data %s\n", extname);
+        psFree (outhead);
+        psFree(table);
+        return false;
+    }
+    psFree (outhead);
+    psFree (table);
+    return true;
+}
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_CMF_PS1_SV1.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_CMF_PS1_SV1.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_CMF_PS1_SV1.c	(revision 29060)
@@ -28,15 +28,24 @@
 #include "pmFPAfile.h"
 
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmGrowthCurve.h"
-#include "pmResiduals.h"
-#include "pmTrend2D.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
-#include "pmSource.h"
-#include "pmModelClass.h"
+#include "pmPSFtry.h"
+
 #include "pmSourceIO.h"
 
@@ -46,5 +55,5 @@
 
 // NOTE: this output function is intended for psphotStack analysis: it includes per-psf radial fluxes 
-// XXX currently in the 'read' function is NOT consistent with the 'write' function (does not read radial fluxes)
+// XXX currently, the 'read' function is NOT consistent with the 'write' function (does not read radial fluxes)
 
 bool pmSourcesWrite_CMF_PS1_SV1 (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe)
@@ -62,4 +71,5 @@
     psF32 errMag, chisq, apRadius;
     psS32 nPix, nDOF;
+    char keyword1[80], keyword2[80];
 
     pmChip *chip = readout->parent->parent;
@@ -95,7 +105,18 @@
     // we use this just to define the output vectors (which must be present for all objects)
     bool status = false;
+    psVector *radMin = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.LOWER");
     psVector *radMax = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.UPPER");
     psAssert (radMax, "this must have been defined and tested earlier!");
     psAssert (radMax->n, "this must have been defined and tested earlier!");
+    psAssert (radMin->n == radMax->n, "inconsistent annular bins");
+
+    // write the radial profile apertures to header
+    for (int i = 0; i < radMax->n; i++) {
+      sprintf (keyword1, "RMIN_%02d", i);
+      sprintf (keyword2, "RMAX_%02d", i);
+      psMetadataAddF32 (imageHeader, PS_LIST_TAIL, keyword1, PS_META_REPLACE, "min radius for SB profile", radMin->data.F32[i]);
+      psMetadataAddF32 (imageHeader, PS_LIST_TAIL, keyword2, PS_META_REPLACE, "min radius for SB profile", radMax->data.F32[i]);
+    }
+
 
     // we write out PSF-fits for all sources, regardless of quality.  the source flags tell us the state
@@ -177,4 +198,5 @@
         psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_FLUX_SIG",PS_DATA_F32, "Sigma of PSF instrumental flux",             source->psfFluxErr);
         psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG",           PS_DATA_F32, "magnitude in standard aperture",             source->apMag);
+        psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG_RAW",       PS_DATA_F32, "magnitude in reported aperture",             source->apMagRaw);
         psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG_RADIUS",    PS_DATA_F32, "radius used for aperture mags",              apRadius);
         psMetadataAdd (row, PS_LIST_TAIL, "PEAK_FLUX_AS_MAG", PS_DATA_F32, "Peak flux expressed as magnitude",           peakMag);
@@ -193,17 +215,46 @@
         psMetadataAdd (row, PS_LIST_TAIL, "PSF_MINOR",        PS_DATA_F32, "PSF width (minor axis)",                     axes.minor);
         psMetadataAdd (row, PS_LIST_TAIL, "PSF_THETA",        PS_DATA_F32, "PSF orientation angle",                      axes.theta);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF",           PS_DATA_F32, "PSF coverage/quality factor",                source->pixWeight);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF",           PS_DATA_F32, "PSF coverage/quality factor (bad)",          source->pixWeightNotBad);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF_PERFECT",   PS_DATA_F32, "PSF coverage/quality factor (poor)",         source->pixWeightNotPoor);
         psMetadataAdd (row, PS_LIST_TAIL, "PSF_NDOF",         PS_DATA_S32, "degrees of freedom",                         nDOF);
         psMetadataAdd (row, PS_LIST_TAIL, "PSF_NPIX",         PS_DATA_S32, "number of pixels in fit",                    nPix);
 
         // distinguish moments measure from window vs S/N > XX ??
-        float mxx = source->moments ? source->moments->Mxx : NAN;
-        float mxy = source->moments ? source->moments->Mxy : NAN;
-        float myy = source->moments ? source->moments->Myy : NAN;
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_XX",       PS_DATA_F32, "second moments (X^2)",                      mxx);
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_XY",       PS_DATA_F32, "second moments (X*Y)",                      mxy);
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_YY",       PS_DATA_F32, "second moments (Y*Y)",                      myy);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "FLAGS",            PS_DATA_U32, "psphot analysis flags",                      source->mode);
+        float Mxx = source->moments ? source->moments->Mxx : NAN;
+        float Mxy = source->moments ? source->moments->Mxy : NAN;
+        float Myy = source->moments ? source->moments->Myy : NAN;
+
+        float Mrf  = source->moments ? source->moments->Mrf : NAN;
+        float Mrh  = source->moments ? source->moments->Mrh : NAN;
+        float Krf  = source->moments ? source->moments->KronFlux : NAN;
+        float dKrf = source->moments ? source->moments->KronFluxErr : NAN;
+
+        float Kinner = source->moments ? source->moments->KronFinner : NAN;
+        float Kouter = source->moments ? source->moments->KronFouter : NAN;
+
+        float M_c3 = source->moments ? 1.0*source->moments->Mxxx - 3.0*source->moments->Mxyy : NAN;
+        float M_s3 = source->moments ? 3.0*source->moments->Mxxy - 1.0*source->moments->Myyy : NAN;
+        float M_c4 = source->moments ? 1.0*source->moments->Mxxxx - 6.0*source->moments->Mxxyy + 1.0*source->moments->Myyyy : NAN;
+        float M_s4 = source->moments ? 4.0*source->moments->Mxxxy - 4.0*source->moments->Mxyyy : NAN;
+
+        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_XX",       PS_DATA_F32, "second moments (X^2)",                      Mxx);
+        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_XY",       PS_DATA_F32, "second moments (X*Y)",                      Mxy);
+        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_YY",       PS_DATA_F32, "second moments (Y*Y)",                      Myy);
+
+        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M3C",      PS_DATA_F32, "third momemt cos theta",                    M_c3);
+        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M3S",      PS_DATA_F32, "third momemt sin theta",                    M_s3);
+        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M4C",      PS_DATA_F32, "fourth momemt cos theta",                   M_c4);
+        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M4S",      PS_DATA_F32, "fourth momemt sin theta",                   M_s4);
+
+        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_R1",       PS_DATA_F32, "first radial moment",                       Mrf);
+        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_RH",       PS_DATA_F32, "half radial moment",                        Mrh);
+        psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX",        PS_DATA_F32, "Kron Flux (in 2.5 R1)",                     Krf);
+        psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX_ERR",    PS_DATA_F32, "Kron Flux Error",                          dKrf);
+
+        psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX_INNER",  PS_DATA_F32, "Kron Flux (in 2.5 R1)",                     Kinner);
+        psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX_OUTER",  PS_DATA_F32, "Kron Flux (in 2.5 R1)",                     Kouter);
+
+        psMetadataAdd (row, PS_LIST_TAIL, "FLAGS",            PS_DATA_U32, "psphot analysis flags",                     source->mode);
+        psMetadataAdd (row, PS_LIST_TAIL, "FLAGS2",           PS_DATA_U32, "psphot analysis flags",                     source->mode2);
 
 	psVector *radFlux    = psVectorAlloc(radMax->n, PS_TYPE_F32);
@@ -352,9 +403,16 @@
         source->peak = pmPeakAlloc(PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], peakFlux, PM_PEAK_LONE);
         source->peak->flux = peakFlux;
+        source->peak->xf   = PAR[PM_PAR_XPOS]; // more accurate position
+        source->peak->yf   = PAR[PM_PAR_YPOS]; // more accurate position
         source->peak->dx   = dPAR[PM_PAR_XPOS];
         source->peak->dy   = dPAR[PM_PAR_YPOS];
-        source->peak->SN   = sqrt(source->peak->flux); // XXX a proxy: various functions sort by peak S/N
-
-        source->pixWeight = psMetadataLookupF32 (&status, row, "PSF_QF");
+        if (isfinite (source->errMag) && (source->errMag > 0.0)) {
+          source->peak->SN = 1.0 / source->errMag;
+        } else {
+          source->peak->SN = sqrt(source->peak->flux); // an alternate proxy: various functions sort by peak S/N
+        }
+
+        source->pixWeightNotBad = psMetadataLookupF32 (&status, row, "PSF_QF");
+        source->pixWeightNotPoor = psMetadataLookupF32 (&status, row, "PSF_QF_PERFECT");
         source->crNsigma  = psMetadataLookupF32 (&status, row, "CR_NSIGMA");
         source->extNsigma = psMetadataLookupF32 (&status, row, "EXT_NSIGMA");
@@ -371,5 +429,27 @@
         source->moments->Myy = psMetadataLookupF32 (&status, row, "MOMENTS_YY");
 
+        source->moments->Mrf         = psMetadataLookupF32 (&status, row, "MOMENTS_R1");
+        source->moments->Mrh         = psMetadataLookupF32 (&status, row, "MOMENTS_RH");
+        source->moments->KronFlux    = psMetadataLookupF32 (&status, row, "KRON_FLUX");
+        source->moments->KronFluxErr = psMetadataLookupF32 (&status, row, "KRON_FLUX_ERR");
+
+        source->moments->KronFinner  = psMetadataLookupF32 (&status, row, "KRON_FLUX_INNER");
+        source->moments->KronFouter  = psMetadataLookupF32 (&status, row, "KRON_FLUX_OUTER");
+
+	// XXX we do not save all of the 3rd and 4th moment parameters. when we load in data,
+	// we are storing enough information so the output will be consistent with the input
+        source->moments->Mxxx = +1.0 * psMetadataLookupF32 (&status, row, "MOMENTS_M3C");
+        source->moments->Mxxy = 0.0;
+        source->moments->Mxyy = 0.0;
+        source->moments->Myyy = -1.0 * psMetadataLookupF32 (&status, row, "MOMENTS_M3S");
+
+        source->moments->Mxxxx = +1.00 * psMetadataLookupF32 (&status, row, "MOMENTS_M4C");
+        source->moments->Mxxxy = 0.0;
+        source->moments->Mxxyy = 0.0;
+        source->moments->Mxyyy = -0.25 * psMetadataLookupF32 (&status, row, "MOMENTS_M4S");
+        source->moments->Myyyy = 0.0;
+
         source->mode = psMetadataLookupU32 (&status, row, "FLAGS");
+        source->mode2 = psMetadataLookupU32 (&status, row, "FLAGS2");
         assert (status);
 
@@ -391,4 +471,5 @@
     psF32 xErr, yErr;
     int nRow = -1;
+    char keyword1[80], keyword2[80];
 
     // create a header to hold the output data
@@ -422,6 +503,4 @@
     bool doAnnuli       = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ANNULI");
     bool doPetrosian    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_PETROSIAN");
-    // bool doIsophotal    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ISOPHOTAL");
-    // bool doKron         = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_KRON");
 
     psVector *radMin = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.LOWER");
@@ -429,24 +508,14 @@
     psAssert (radMin->n == radMax->n, "inconsistent annular bins");
 
-    // int nRadialBins = 0;
-    // if (doAnnuli) {
-    // 	// get the max count of radial bins
-    // 	for (int i = 0; i < sources->n; i++) {
-    // 	    pmSource *source = sources->data[i];
-    // 	    if (!source->extpars) continue;
-    // 	    if (!source->extpars->radProfile ) continue;
-    //         if (!source->extpars->radProfile->binSB) continue;
-    // 	    nRadialBins = PS_MAX(nRadialBins, source->extpars->radProfile->binSB->n);
-    // 	}
-    // }
+    // write the radial profile apertures to header
+    for (int i = 0; i < radMax->n; i++) {
+      sprintf (keyword1, "RMIN_%02d", i);
+      sprintf (keyword2, "RMAX_%02d", i);
+      psMetadataAddF32 (outhead, PS_LIST_TAIL, keyword1, PS_META_REPLACE, "min radius for SB profile", radMin->data.F32[i]);
+      psMetadataAddF32 (outhead, PS_LIST_TAIL, keyword2, PS_META_REPLACE, "min radius for SB profile", radMax->data.F32[i]);
+    }
 
     // we write out all sources, regardless of quality.  the source flags tell us the state
     for (int i = 0; i < sources->n; i++) {
-        // skip source if it is not a ext sourc
-        // XXX we have two places that extended source parameters are measured:
-        // psphotExtendedSources, which measures the aperture-like parameters and (potentially) the psf-convolved extended source models,
-        // psphotFitEXT, which does the simple extended source model fit (not psf-convolved)
-        // should we require both?
-
         pmSource *source = sources->data[i];
 
@@ -514,39 +583,4 @@
         }
 
-# if (0)
-        // Kron measurements
-        if (doKron) {
-            pmSourceKronValues *kron = source->extpars->kron;
-            if (kron) {
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_MAG",        PS_DATA_F32, "Kron Magnitude",       kron->mag);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_MAG_ERR",    PS_DATA_F32, "Kron Magnitude Error", kron->magErr);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_RADIUS",     PS_DATA_F32, "Kron Radius",          kron->rad);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_RADIUS_ERR", PS_DATA_F32, "Kron Radius Error",    kron->radErr);
-            } else {
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_MAG",        PS_DATA_F32, "Kron Magnitude",       NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_MAG_ERR",    PS_DATA_F32, "Kron Magnitude Error", NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_RADIUS",     PS_DATA_F32, "Kron Radius",          NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_RADIUS_ERR", PS_DATA_F32, "Kron Radius Error",    NAN);
-            }
-        }
-
-        // Isophot measurements
-        // XXX insert header data: isophotal level
-        if (doIsophotal) {
-            pmSourceIsophotalValues *isophot = source->extpars->isophot;
-            if (isophot) {
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_MAG",        PS_DATA_F32, "Isophot Magnitude",       isophot->mag);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_MAG_ERR",    PS_DATA_F32, "Isophot Magnitude Error", isophot->magErr);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_RADIUS",     PS_DATA_F32, "Isophot Radius",          isophot->rad);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_RADIUS_ERR", PS_DATA_F32, "Isophot Radius Error",    isophot->radErr);
-            } else {
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_MAG",        PS_DATA_F32, "Isophot Magnitude",       NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_MAG_ERR",    PS_DATA_F32, "Isophot Magnitude Error", NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_RADIUS",     PS_DATA_F32, "Isophot Radius",          NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_RADIUS_ERR", PS_DATA_F32, "Isophot Radius Error",    NAN);
-            }
-        }
-# endif
-
         // Flux Annuli (if we have extended source measurements, we have these.  only optionally save them)
         if (doAnnuli) {
@@ -616,5 +650,5 @@
 
 // XXX this layout is still the same as PS1_DEV_1
-bool pmSourcesWrite_CMF_PS1_SV1_XFIT (psFits *fits, pmReadout *readout, psArray *sources, char *extname)
+bool pmSourcesWrite_CMF_PS1_SV1_XFIT(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname)
 {
 
@@ -665,4 +699,7 @@
             assert (model);
 
+	    // skip models which were not actually fitted
+	    if (model->flags & PM_MODEL_STATUS_BADARGS) continue;
+
             PAR = model->params->data.F32;
             dPAR = model->dparams->data.F32;
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_CMF_PS1_V1.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_CMF_PS1_V1.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_CMF_PS1_V1.c	(revision 29060)
@@ -28,15 +28,24 @@
 #include "pmFPAfile.h"
 
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmGrowthCurve.h"
-#include "pmResiduals.h"
-#include "pmTrend2D.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
-#include "pmSource.h"
-#include "pmModelClass.h"
+#include "pmPSFtry.h"
+
 #include "pmSourceIO.h"
 
@@ -45,6 +54,5 @@
 // followed by a zero-size matrix, followed by the table data
 
-bool pmSourcesWrite_CMF_PS1_V1 (psFits *fits, pmReadout *readout, psArray *sources,
-                                psMetadata *imageHeader, psMetadata *tableHeader, char *extname)
+bool pmSourcesWrite_CMF_PS1_V1 (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe)
 {
     PS_ASSERT_PTR_NON_NULL(fits, false);
@@ -184,5 +192,5 @@
         psMetadataAdd (row, PS_LIST_TAIL, "PSF_MINOR",        PS_DATA_F32, "PSF width (minor axis)",                     axes.minor);
         psMetadataAdd (row, PS_LIST_TAIL, "PSF_THETA",        PS_DATA_F32, "PSF orientation angle",                      axes.theta);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF",           PS_DATA_F32, "PSF coverage/quality factor",                source->pixWeight);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF",           PS_DATA_F32, "PSF coverage/quality factor",                source->pixWeightNotBad);
         psMetadataAdd (row, PS_LIST_TAIL, "PSF_NDOF",         PS_DATA_S32, "degrees of freedom",                         nDOF);
         psMetadataAdd (row, PS_LIST_TAIL, "PSF_NPIX",         PS_DATA_S32, "number of pixels in fit",                    nPix);
@@ -310,8 +318,15 @@
         source->peak = pmPeakAlloc(PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], peakFlux, PM_PEAK_LONE);
         source->peak->flux = peakFlux;
+        source->peak->xf   = PAR[PM_PAR_XPOS]; // more accurate position
+        source->peak->yf   = PAR[PM_PAR_YPOS]; // more accurate position
         source->peak->dx   = dPAR[PM_PAR_XPOS];
         source->peak->dy   = dPAR[PM_PAR_YPOS];
-
-        source->pixWeight = psMetadataLookupF32 (&status, row, "PSF_QF");
+        if (isfinite (source->errMag) && (source->errMag > 0.0)) {
+          source->peak->SN = 1.0 / source->errMag;
+        } else {
+          source->peak->SN = sqrt(source->peak->flux); // an alternate proxy: various functions sort by peak S/N
+        }
+
+        source->pixWeightNotBad = psMetadataLookupF32 (&status, row, "PSF_QF");
         source->crNsigma  = psMetadataLookupF32 (&status, row, "CR_NSIGMA");
         source->extNsigma = psMetadataLookupF32 (&status, row, "EXT_NSIGMA");
@@ -516,5 +531,5 @@
 
 // XXX this layout is still the same as PS1_DEV_1
-bool pmSourcesWrite_CMF_PS1_V1_XFIT (psFits *fits, pmReadout *readout, psArray *sources, char *extname)
+bool pmSourcesWrite_CMF_PS1_V1_XFIT(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname)
 {
 
@@ -565,4 +580,7 @@
             assert (model);
 
+	    // skip models which were not actually fitted
+	    if (model->flags & PM_MODEL_STATUS_BADARGS) continue;
+
             PAR = model->params->data.F32;
             dPAR = model->dparams->data.F32;
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_CMF_PS1_V2.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_CMF_PS1_V2.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_CMF_PS1_V2.c	(revision 29060)
@@ -28,15 +28,24 @@
 #include "pmFPAfile.h"
 
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmGrowthCurve.h"
-#include "pmResiduals.h"
-#include "pmTrend2D.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
-#include "pmSource.h"
-#include "pmModelClass.h"
+#include "pmPSFtry.h"
+
 #include "pmSourceIO.h"
 
@@ -45,5 +54,5 @@
 // followed by a zero-size matrix, followed by the table data
 
-bool pmSourcesWrite_CMF_PS1_V2 (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname)
+bool pmSourcesWrite_CMF_PS1_V2 (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe)
 {
     PS_ASSERT_PTR_NON_NULL(fits, false);
@@ -182,5 +191,5 @@
         psMetadataAdd (row, PS_LIST_TAIL, "PSF_MINOR",        PS_DATA_F32, "PSF width (minor axis)",                     axes.minor);
         psMetadataAdd (row, PS_LIST_TAIL, "PSF_THETA",        PS_DATA_F32, "PSF orientation angle",                      axes.theta);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF",           PS_DATA_F32, "PSF coverage/quality factor",                source->pixWeight);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF",           PS_DATA_F32, "PSF coverage/quality factor",                source->pixWeightNotBad);
         psMetadataAdd (row, PS_LIST_TAIL, "PSF_NDOF",         PS_DATA_S32, "degrees of freedom",                         nDOF);
         psMetadataAdd (row, PS_LIST_TAIL, "PSF_NPIX",         PS_DATA_S32, "number of pixels in fit",                    nPix);
@@ -314,9 +323,15 @@
         source->peak = pmPeakAlloc(PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], peakFlux, PM_PEAK_LONE);
         source->peak->flux = peakFlux;
+        source->peak->xf   = PAR[PM_PAR_XPOS]; // more accurate position
+        source->peak->yf   = PAR[PM_PAR_YPOS]; // more accurate position
         source->peak->dx   = dPAR[PM_PAR_XPOS];
         source->peak->dy   = dPAR[PM_PAR_YPOS];
-        source->peak->SN   = sqrt(source->peak->flux); // XXX a proxy: various functions sort by peak S/N
-
-        source->pixWeight = psMetadataLookupF32 (&status, row, "PSF_QF");
+        if (isfinite (source->errMag) && (source->errMag > 0.0)) {
+          source->peak->SN = 1.0 / source->errMag;
+        } else {
+          source->peak->SN = sqrt(source->peak->flux); // an alternate proxy: various functions sort by peak S/N
+        }
+
+        source->pixWeightNotBad = psMetadataLookupF32 (&status, row, "PSF_QF");
         source->crNsigma  = psMetadataLookupF32 (&status, row, "CR_NSIGMA");
         source->extNsigma = psMetadataLookupF32 (&status, row, "EXT_NSIGMA");
@@ -578,5 +593,5 @@
 
 // XXX this layout is still the same as PS1_DEV_1
-bool pmSourcesWrite_CMF_PS1_V2_XFIT (psFits *fits, pmReadout *readout, psArray *sources, char *extname)
+bool pmSourcesWrite_CMF_PS1_V2_XFIT(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname)
 {
 
@@ -627,4 +642,7 @@
             assert (model);
 
+	    // skip models which were not actually fitted
+	    if (model->flags & PM_MODEL_STATUS_BADARGS) continue;
+
             PAR = model->params->data.F32;
             dPAR = model->dparams->data.F32;
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_CMF_PS1_V3.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_CMF_PS1_V3.c	(revision 29060)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_CMF_PS1_V3.c	(revision 29060)
@@ -0,0 +1,733 @@
+/** @file  pmSourceIO.c
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2009-02-18 02:44:19 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmDetrendDB.h"
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+#include "pmFPAview.h"
+#include "pmFPAfile.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmSpan.h"
+#include "pmFootprintSpans.h"
+#include "pmFootprint.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
+#include "pmPSF.h"
+#include "pmPSFtry.h"
+
+#include "pmSourceIO.h"
+
+// panstarrs-style FITS table output (header + table in 1st extension)
+// this format consists of a header derived from the image header
+// followed by a zero-size matrix, followed by the table data
+
+bool pmSourcesWrite_CMF_PS1_V3 (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe)
+{
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_PTR_NON_NULL(sources, false);
+    PS_ASSERT_PTR_NON_NULL(extname, false);
+
+    psArray *table;
+    psMetadata *row;
+    psF32 *PAR, *dPAR;
+    psEllipseAxes axes;
+    psF32 xPos, yPos;
+    psF32 xErr, yErr;
+    psF32 errMag, chisq, apRadius;
+    psS32 nPix, nDOF;
+
+    pmChip *chip = readout->parent->parent;
+    pmFPA  *fpa  = chip->parent;
+
+    bool status1 = false;
+    bool status2 = false;
+    float magOffset = NAN;
+    float exptime   = psMetadataLookupF32 (&status1, fpa->concepts, "FPA.EXPOSURE");
+    float zeropt    = psMetadataLookupF32(&status2, fpa->concepts, "FPA.ZP");
+    if (!isfinite(zeropt)) {
+        zeropt    = psMetadataLookupF32 (&status2, imageHeader, "ZPT_OBS");
+    }
+    if (status1 && status2 && (exptime > 0.0)) {
+        magOffset = zeropt + 2.5*log10(exptime);
+    }
+    float zeroptErr = psMetadataLookupF32 (&status2, imageHeader, "ZPT_ERR");
+
+    // if the sequence is defined, write these in seq order; otherwise
+    // write them in S/N order:
+    if (sources->n > 0) {
+        pmSource *source = (pmSource *) sources->data[0];
+        if (source->seq == -1) {
+	    // let's write these out in S/N order
+	    sources = psArraySort (sources, pmSourceSortBySN);
+        } else {
+	    sources = psArraySort (sources, pmSourceSortBySeq);
+        }
+    }
+
+    table = psArrayAllocEmpty (sources->n);
+
+    // we write out PSF-fits for all sources, regardless of quality.  the source flags tell us the state
+    // by the time we call this function, all values should be assigned.  let's use asserts to be sure in some cases.
+    for (int i = 0; i < sources->n; i++) {
+        pmSource *source = (pmSource *) sources->data[i];
+
+        // If source->seq is -1, source was generated in this analysis.  If source->seq is
+        // not -1, source was read from elsewhere: in the latter case, preserve the source
+        // ID.  source.seq is used instead of source.id since the latter is a const
+        // generated on Alloc, and would thus be wrong for read in sources.
+        if (source->seq == -1) {
+	    source->seq = i;
+        }
+
+        // no difference between PSF and non-PSF model
+        pmModel *model = source->modelPSF;
+
+        if (model != NULL) {
+            PAR = model->params->data.F32;
+            dPAR = model->dparams->data.F32;
+            xPos = PAR[PM_PAR_XPOS];
+            yPos = PAR[PM_PAR_YPOS];
+            if (source->mode & PM_SOURCE_MODE_NONLINEAR_FIT) {
+		xErr = dPAR[PM_PAR_XPOS];
+		yErr = dPAR[PM_PAR_YPOS];
+            } else {
+		// in linear-fit mode, there is no error on the centroid
+		xErr = source->peak->dx;
+		yErr = source->peak->dy;
+            }
+            if (isfinite(PAR[PM_PAR_SXX]) && isfinite(PAR[PM_PAR_SXX]) && isfinite(PAR[PM_PAR_SXX])) {
+                axes = pmPSF_ModelToAxes (PAR, 20.0);
+            } else {
+                axes.major = NAN;
+                axes.minor = NAN;
+                axes.theta = NAN;
+            }
+            chisq = model->chisq;
+            nDOF = model->nDOF;
+            nPix = model->nPix;
+            apRadius = source->apRadius;
+            errMag = model->dparams->data.F32[PM_PAR_I0] / model->params->data.F32[PM_PAR_I0];
+        } else {
+            xPos = source->peak->xf;
+            yPos = source->peak->yf;
+            xErr = source->peak->dx;
+            yErr = source->peak->dy;
+            axes.major = NAN;
+            axes.minor = NAN;
+            axes.theta = NAN;
+            chisq = NAN;
+            nDOF = 0;
+            nPix = 0;
+            apRadius = NAN;
+            errMag = NAN;
+        }
+
+        float calMag = isfinite(magOffset) ? source->psfMag + magOffset : NAN;
+        float peakMag = (source->peak->flux > 0) ? -2.5*log10(source->peak->flux) : NAN;
+        psS16 nImageOverlap = 1;
+
+        psSphere ptSky = {0.0, 0.0, 0.0, 0.0};
+        float posAngle = 0.0;
+        float pltScale = 0.0;
+        pmSourceLocalAstrometry (&ptSky, &posAngle, &pltScale, chip, xPos, yPos);
+
+        row = psMetadataAlloc ();
+        psMetadataAdd (row, PS_LIST_TAIL, "IPP_IDET",         PS_DATA_U32, "IPP detection identifier index",             source->seq);
+        psMetadataAdd (row, PS_LIST_TAIL, "X_PSF",            PS_DATA_F32, "PSF x coordinate",                           xPos);
+        psMetadataAdd (row, PS_LIST_TAIL, "Y_PSF",            PS_DATA_F32, "PSF y coordinate",                           yPos);
+        psMetadataAdd (row, PS_LIST_TAIL, "X_PSF_SIG",        PS_DATA_F32, "Sigma in PSF x coordinate",                  xErr); // XXX this is only measured for non-linear fits
+        psMetadataAdd (row, PS_LIST_TAIL, "Y_PSF_SIG",        PS_DATA_F32, "Sigma in PSF y coordinate",                  yErr); // XXX this is only measured for non-linear fits
+        psMetadataAdd (row, PS_LIST_TAIL, "POSANGLE",         PS_DATA_F32, "position angle at source (degrees)",         posAngle*PS_DEG_RAD);
+        psMetadataAdd (row, PS_LIST_TAIL, "PLTSCALE",         PS_DATA_F32, "plate scale at source (arcsec/pixel)",       pltScale*PS_DEG_RAD*3600.0);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_MAG",     PS_DATA_F32, "PSF fit instrumental magnitude",             source->psfMag);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_MAG_SIG", PS_DATA_F32, "Sigma of PSF instrumental magnitude",        errMag);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_FLUX",    PS_DATA_F32, "PSF fit instrumental flux (counts)",         source->psfFlux);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_FLUX_SIG",PS_DATA_F32, "Sigma of PSF instrumental flux",             source->psfFluxErr);
+        psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG",           PS_DATA_F32, "magnitude in standard aperture",             source->apMag);
+        psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG_RAW",       PS_DATA_F32, "magnitude in reported aperture",             source->apMagRaw);
+        psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG_RADIUS",    PS_DATA_F32, "radius used for aperture mags",              apRadius);
+        psMetadataAdd (row, PS_LIST_TAIL, "PEAK_FLUX_AS_MAG", PS_DATA_F32, "Peak flux expressed as magnitude",           peakMag);
+        psMetadataAdd (row, PS_LIST_TAIL, "CAL_PSF_MAG",      PS_DATA_F32, "PSF Magnitude using supplied calibration",   calMag);
+        psMetadataAdd (row, PS_LIST_TAIL, "CAL_PSF_MAG_SIG",  PS_DATA_F32, "measured scatter of zero point calibration", zeroptErr);
+        psMetadataAdd (row, PS_LIST_TAIL, "RA_PSF",           PS_DATA_F64, "PSF RA coordinate (degrees)",                ptSky.r*PS_DEG_RAD);
+        psMetadataAdd (row, PS_LIST_TAIL, "DEC_PSF",          PS_DATA_F64, "PSF DEC coordinate (degrees)",               ptSky.d*PS_DEG_RAD);
+        psMetadataAdd (row, PS_LIST_TAIL, "SKY",              PS_DATA_F32, "Sky level",                                  source->sky);
+        psMetadataAdd (row, PS_LIST_TAIL, "SKY_SIGMA",        PS_DATA_F32, "Sigma of sky level",                         source->skyErr);
+
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_CHISQ",        PS_DATA_F32, "Chisq of PSF-fit",                           chisq);
+        psMetadataAdd (row, PS_LIST_TAIL, "CR_NSIGMA",        PS_DATA_F32, "Nsigma deviations from PSF to CF",           source->crNsigma);
+        psMetadataAdd (row, PS_LIST_TAIL, "EXT_NSIGMA",       PS_DATA_F32, "Nsigma deviations from PSF to EXT",          source->extNsigma);
+
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_MAJOR",        PS_DATA_F32, "PSF width (major axis)",                     axes.major);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_MINOR",        PS_DATA_F32, "PSF width (minor axis)",                     axes.minor);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_THETA",        PS_DATA_F32, "PSF orientation angle",                      axes.theta);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF",           PS_DATA_F32, "PSF coverage/quality factor (bad)",          source->pixWeightNotBad);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF_PERFECT",   PS_DATA_F32, "PSF coverage/quality factor (poor)",         source->pixWeightNotPoor);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_NDOF",         PS_DATA_S32, "degrees of freedom",                         nDOF);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_NPIX",         PS_DATA_S32, "number of pixels in fit",                    nPix);
+
+        // distinguish moments measure from window vs S/N > XX ??
+        float Mxx = source->moments ? source->moments->Mxx : NAN;
+        float Mxy = source->moments ? source->moments->Mxy : NAN;
+        float Myy = source->moments ? source->moments->Myy : NAN;
+
+        float Mrf  = source->moments ? source->moments->Mrf : NAN;
+        float Mrh  = source->moments ? source->moments->Mrh : NAN;
+        float Krf  = source->moments ? source->moments->KronFlux : NAN;
+        float dKrf = source->moments ? source->moments->KronFluxErr : NAN;
+
+        float Kinner = source->moments ? source->moments->KronFinner : NAN;
+        float Kouter = source->moments ? source->moments->KronFouter : NAN;
+
+        float M_c3 = source->moments ? 1.0*source->moments->Mxxx - 3.0*source->moments->Mxyy : NAN;
+        float M_s3 = source->moments ? 3.0*source->moments->Mxxy - 1.0*source->moments->Myyy : NAN;
+        float M_c4 = source->moments ? 1.0*source->moments->Mxxxx - 6.0*source->moments->Mxxyy + 1.0*source->moments->Myyyy : NAN;
+        float M_s4 = source->moments ? 4.0*source->moments->Mxxxy - 4.0*source->moments->Mxyyy : NAN;
+
+        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_XX",       PS_DATA_F32, "second moments (X^2)",                      Mxx);
+        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_XY",       PS_DATA_F32, "second moments (X*Y)",                      Mxy);
+        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_YY",       PS_DATA_F32, "second moments (Y*Y)",                      Myy);
+
+        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M3C",      PS_DATA_F32, "third momemt cos theta",                    M_c3);
+        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M3S",      PS_DATA_F32, "third momemt sin theta",                    M_s3);
+        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M4C",      PS_DATA_F32, "fourth momemt cos theta",                   M_c4);
+        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M4S",      PS_DATA_F32, "fourth momemt sin theta",                   M_s4);
+
+        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_R1",       PS_DATA_F32, "first radial moment",                       Mrf);
+        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_RH",       PS_DATA_F32, "half radial moment",                        Mrh);
+        psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX",        PS_DATA_F32, "Kron Flux (in 2.5 R1)",                     Krf);
+        psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX_ERR",    PS_DATA_F32, "Kron Flux Error",                          dKrf);
+
+        psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX_INNER",  PS_DATA_F32, "Kron Flux (in 2.5 R1)",                     Kinner);
+        psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX_OUTER",  PS_DATA_F32, "Kron Flux (in 2.5 R1)",                     Kouter);
+
+        psMetadataAdd (row, PS_LIST_TAIL, "FLAGS",            PS_DATA_U32, "psphot analysis flags",                     source->mode);
+        psMetadataAdd (row, PS_LIST_TAIL, "FLAGS2",           PS_DATA_U32, "psphot analysis flags",                     source->mode2);
+
+        // XXX not sure how to get this : need to load Nimages with weight?
+        psMetadataAdd (row, PS_LIST_TAIL, "N_FRAMES",         PS_DATA_U16, "Number of frames overlapping source center", nImageOverlap);
+        psMetadataAdd (row, PS_LIST_TAIL, "PADDING",          PS_DATA_S16, "padding", 0);
+
+        psArrayAdd (table, 100, row);
+        psFree (row);
+
+        // EXT_NSIGMA will be NAN if: 1) contour ellipse is imaginary; 2) source is not
+        // subtracted
+
+        // CR_NSIGMA will be NAN if: 1) source is not subtracted; 2) source is on the image
+        // edge; 3) any pixels in the 3x3 peak region are masked;
+    }
+
+    // XXX why do we make a copy here to be supplemented with the masks?  why not do this in the calling function?
+    psMetadata *header = psMetadataCopy(NULL, tableHeader);
+    pmSourceMasksHeader(header);
+
+    if (table->n == 0) {
+        if (!psFitsWriteBlank(fits, header, extname)) {
+            psError(psErrorCodeLast(), false, "Unable to write blank sources file.");
+            psFree(table);
+            psFree(header);
+            return false;
+        }
+        psFree(table);
+        psFree(header);
+        return true;
+    }
+
+    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
+    if (!psFitsWriteTable(fits, header, table, extname)) {
+        psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
+        psFree(table);
+        psFree(header);
+        return false;
+    }
+    psFree(table);
+    psFree(header);
+
+    return true;
+}
+
+// read in a readout from the fits file
+psArray *pmSourcesRead_CMF_PS1_V3 (psFits *fits, psMetadata *header)
+{
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_PTR_NON_NULL(header, false);
+
+    bool status;
+    psF32 *PAR, *dPAR;
+    psEllipseAxes axes;
+
+    // define PSF model type
+    // XXX need to carry the extra model parameters
+    int modelType = pmModelClassGetType ("PS_MODEL_GAUSS");
+
+    char *PSF_NAME = psMetadataLookupStr (&status, header, "PSF_NAME");
+    if (PSF_NAME != NULL) {
+        modelType = pmModelClassGetType (PSF_NAME);
+    }
+    assert (modelType > -1);
+
+    // We get the size of the table, and allocate the array of sources first because the table
+    // is large and ephemeral --- when the table gets blown away, whatever is allocated after
+    // the table is read blocks the free.  In fact, it's better to read the table row by row.
+    long numSources = psFitsTableSize(fits); // Number of sources in table
+    psArray *sources = psArrayAlloc(numSources); // Array of sources, to return
+
+    // convert the table to the pmSource entriesa
+    for (int i = 0; i < numSources; i++) {
+        psMetadata *row = psFitsReadTableRow(fits, i); // Table row
+        if (!row) {
+            psError(psErrorCodeLast(), false, "Unable to read row %d of sources", i);
+            psFree(sources);
+            return NULL;
+        }
+
+        pmSource *source = pmSourceAlloc ();
+        pmModel *model = pmModelAlloc (modelType);
+        source->modelPSF  = model;
+        source->type = PM_SOURCE_TYPE_STAR; // XXX this should be added to the flags
+
+        // NOTE: A SEGV here because "model" is NULL is probably caused by not initialising the models.
+        PAR = model->params->data.F32;
+        dPAR = model->dparams->data.F32;
+
+        source->seq       = psMetadataLookupU32 (&status, row, "IPP_IDET");
+        PAR[PM_PAR_XPOS]  = psMetadataLookupF32 (&status, row, "X_PSF");
+        PAR[PM_PAR_YPOS]  = psMetadataLookupF32 (&status, row, "Y_PSF");
+        dPAR[PM_PAR_XPOS] = psMetadataLookupF32 (&status, row, "X_PSF_SIG");
+        dPAR[PM_PAR_YPOS] = psMetadataLookupF32 (&status, row, "Y_PSF_SIG");
+        axes.major        = psMetadataLookupF32 (&status, row, "PSF_MAJOR");
+        axes.minor        = psMetadataLookupF32 (&status, row, "PSF_MINOR");
+        axes.theta        = psMetadataLookupF32 (&status, row, "PSF_THETA");
+
+        PAR[PM_PAR_SKY]   = psMetadataLookupF32 (&status, row, "SKY");
+        dPAR[PM_PAR_SKY]  = psMetadataLookupF32 (&status, row, "SKY_SIGMA");
+        source->sky       = PAR[PM_PAR_SKY];
+        source->skyErr    = dPAR[PM_PAR_SKY];
+
+        // XXX use these to determine PAR[PM_PAR_I0]?
+        source->psfMag    = psMetadataLookupF32 (&status, row, "PSF_INST_MAG");
+        source->errMag    = psMetadataLookupF32 (&status, row, "PSF_INST_MAG_SIG");
+        source->apMag     = psMetadataLookupF32 (&status, row, "AP_MAG");
+
+        // XXX this scaling is incorrect: does not include the 2 \pi AREA factor
+        PAR[PM_PAR_I0]    = (isfinite(source->psfMag)) ? pow(10.0, -0.4*source->psfMag) : NAN;
+        dPAR[PM_PAR_I0]   = (isfinite(source->psfMag)) ? PAR[PM_PAR_I0] * source->errMag : NAN;
+
+        pmPSF_AxesToModel (PAR, axes);
+
+        float peakMag     = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
+        float peakFlux    = (isfinite(peakMag)) ? pow(10.0, -0.4*peakMag) : NAN;
+
+        // recreate the peak to match (xPos, yPos) +/- (xErr, yErr)
+        source->peak = pmPeakAlloc(PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], peakFlux, PM_PEAK_LONE);
+        source->peak->flux = peakFlux;
+        source->peak->xf   = PAR[PM_PAR_XPOS]; // more accurate position
+        source->peak->yf   = PAR[PM_PAR_YPOS]; // more accurate position
+        source->peak->dx   = dPAR[PM_PAR_XPOS];
+        source->peak->dy   = dPAR[PM_PAR_YPOS];
+        if (isfinite (source->errMag) && (source->errMag > 0.0)) {
+          source->peak->SN = 1.0 / source->errMag;
+        } else {
+          source->peak->SN = sqrt(source->peak->flux); // an alternate proxy: various functions sort by peak S/N
+        }
+
+        source->pixWeightNotBad = psMetadataLookupF32 (&status, row, "PSF_QF");
+        source->pixWeightNotPoor = psMetadataLookupF32 (&status, row, "PSF_QF_PERFECT");
+        source->crNsigma  = psMetadataLookupF32 (&status, row, "CR_NSIGMA");
+        source->extNsigma = psMetadataLookupF32 (&status, row, "EXT_NSIGMA");
+        source->apRadius  = psMetadataLookupS32 (&status, row, "AP_MAG_RADIUS");
+
+        // note that some older versions used PSF_PROBABILITY: this was not well defined.
+        model->chisq      = psMetadataLookupF32 (&status, row, "PSF_CHISQ");
+        model->nDOF       = psMetadataLookupS32 (&status, row, "PSF_NDOF");
+        model->nPix       = psMetadataLookupS32 (&status, row, "PSF_NPIX");
+
+        source->moments = pmMomentsAlloc ();
+        source->moments->Mxx = psMetadataLookupF32 (&status, row, "MOMENTS_XX");
+        source->moments->Mxy = psMetadataLookupF32 (&status, row, "MOMENTS_XY");
+        source->moments->Myy = psMetadataLookupF32 (&status, row, "MOMENTS_YY");
+
+        source->moments->Mrf         = psMetadataLookupF32 (&status, row, "MOMENTS_R1");
+        source->moments->Mrh         = psMetadataLookupF32 (&status, row, "MOMENTS_RH");
+        source->moments->KronFlux    = psMetadataLookupF32 (&status, row, "KRON_FLUX");
+        source->moments->KronFluxErr = psMetadataLookupF32 (&status, row, "KRON_FLUX_ERR");
+
+        source->moments->KronFinner  = psMetadataLookupF32 (&status, row, "KRON_FLUX_INNER");
+        source->moments->KronFouter  = psMetadataLookupF32 (&status, row, "KRON_FLUX_OUTER");
+
+	// XXX we do not save all of the 3rd and 4th moment parameters. when we load in data,
+	// we are storing enough information so the output will be consistent with the input
+        source->moments->Mxxx = +1.0 * psMetadataLookupF32 (&status, row, "MOMENTS_M3C");
+        source->moments->Mxxy = 0.0;
+        source->moments->Mxyy = 0.0;
+        source->moments->Myyy = -1.0 * psMetadataLookupF32 (&status, row, "MOMENTS_M3S");
+
+        source->moments->Mxxxx = +1.00 * psMetadataLookupF32 (&status, row, "MOMENTS_M4C");
+        source->moments->Mxxxy = 0.0;
+        source->moments->Mxxyy = 0.0;
+        source->moments->Mxyyy = -0.25 * psMetadataLookupF32 (&status, row, "MOMENTS_M4S");
+        source->moments->Myyyy = 0.0;
+
+        source->mode = psMetadataLookupU32 (&status, row, "FLAGS");
+        source->mode2 = psMetadataLookupU32 (&status, row, "FLAGS2");
+        assert (status);
+
+        sources->data[i] = source;
+        psFree(row);
+    }
+
+    return sources;
+}
+
+bool pmSourcesWrite_CMF_PS1_V3_XSRC (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe)
+{
+    bool status;
+    psArray *table;
+    psMetadata *row;
+    psF32 *PAR, *dPAR;
+    psF32 xPos, yPos;
+    psF32 xErr, yErr;
+    int nRow = -1;
+    char keyword1[80], keyword2[80];
+
+    // create a header to hold the output data
+    psMetadata *outhead = psMetadataAlloc ();
+
+    // write the links to the image header
+    psMetadataAddStr (outhead, PS_LIST_TAIL, "EXTNAME", PS_META_REPLACE, "xsrc table extension", extname);
+
+    pmChip *chip = readout->parent->parent;
+    pmFPA  *fpa  = chip->parent;
+
+    // zero point corrections
+    bool status1 = false;
+    bool status2 = false;
+    float magOffset = 0.0;
+    float exptime   = psMetadataLookupF32(&status1, fpa->concepts, "FPA.EXPOSURE");
+    float zeropt    = psMetadataLookupF32(&status2, fpa->concepts, "FPA.ZP");
+    if (!isfinite(zeropt)) {
+        zeropt    = psMetadataLookupF32 (&status2, imageHeader, "ZPT_OBS");
+    }
+    if (status1 && status2 && (exptime > 0.0)) {
+        magOffset = zeropt + 2.5*log10(exptime);
+    }
+
+    // let's write these out in S/N order
+    sources = psArraySort (sources, pmSourceSortBySN);
+
+    table = psArrayAllocEmpty (sources->n);
+
+    // which extended source analyses should we perform?
+    bool doAnnuli       = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ANNULI");
+    bool doPetrosian    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_PETROSIAN");
+
+    psVector *radMin = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.LOWER");
+    psVector *radMax = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.UPPER");
+    psAssert (radMin->n == radMax->n, "inconsistent annular bins");
+
+    // write the radial profile apertures to header
+    for (int i = 0; i < radMax->n; i++) {
+      sprintf (keyword1, "RMIN_%02d", i);
+      sprintf (keyword2, "RMAX_%02d", i);
+      psMetadataAddF32 (outhead, PS_LIST_TAIL, keyword1, PS_META_REPLACE, "min radius for SB profile", radMin->data.F32[i]);
+      psMetadataAddF32 (outhead, PS_LIST_TAIL, keyword2, PS_META_REPLACE, "min radius for SB profile", radMax->data.F32[i]);
+    }
+
+    // we write out all sources, regardless of quality.  the source flags tell us the state
+    for (int i = 0; i < sources->n; i++) {
+        pmSource *source = sources->data[i];
+
+        // skip sources without measurements
+        if (source->extpars == NULL) continue;
+
+        // we require a PSF model fit (ignore the real crud)
+        pmModel *model = source->modelPSF;
+        if (model == NULL) continue;
+
+        // XXX I need to split the extended models from the extended aperture measurements
+        PAR = model->params->data.F32;
+        dPAR = model->dparams->data.F32;
+        xPos = PAR[PM_PAR_XPOS];
+        yPos = PAR[PM_PAR_YPOS];
+        xErr = dPAR[PM_PAR_XPOS];
+        yErr = dPAR[PM_PAR_YPOS];
+
+        row = psMetadataAlloc ();
+
+        // XXX we are not writing out the mode (flags) or the type (psf, ext, etc)
+        psMetadataAdd (row, PS_LIST_TAIL, "IPP_IDET",         PS_DATA_U32, "IPP detection identifier index",             source->seq);
+        psMetadataAdd (row, PS_LIST_TAIL, "X_EXT",            PS_DATA_F32, "EXT model x coordinate",                     xPos);
+        psMetadataAdd (row, PS_LIST_TAIL, "Y_EXT",            PS_DATA_F32, "EXT model y coordinate",                     yPos);
+        psMetadataAdd (row, PS_LIST_TAIL, "X_EXT_SIG",        PS_DATA_F32, "Sigma in EXT x coordinate",                  xErr);
+        psMetadataAdd (row, PS_LIST_TAIL, "Y_EXT_SIG",        PS_DATA_F32, "Sigma in EXT y coordinate",                  yErr);
+
+	float AxialRatio = NAN;
+	float AxialTheta = NAN;
+	pmSourceExtendedPars *extpars = source->extpars;
+	if (extpars) {
+	    AxialRatio = extpars->axes.minor / extpars->axes.major;
+	    AxialTheta = extpars->axes.theta;
+	}
+        psMetadataAdd (row, PS_LIST_TAIL, "F25_ARATIO",       PS_DATA_F32, "Axial Ratio of radial profile",              AxialRatio);
+        psMetadataAdd (row, PS_LIST_TAIL, "F25_THETA",        PS_DATA_F32, "Angle of radial profile ellipse",                  AxialTheta);
+
+        // Petrosian measurements
+        // XXX insert header data: petrosian ref radius, flux ratio
+	// XXX check flags to see if Pet was measured
+        if (doPetrosian) {
+	    pmSourceExtendedPars *extpars = source->extpars;
+            if (extpars) {
+		// XXX note that this mag is either calibrated or instrumental depending on existence of zero point 
+		float mag = (extpars->petrosianFlux > 0.0) ? -2.5*log10(extpars->petrosianFlux) + magOffset : NAN; // XXX zero point
+		float magErr = (extpars->petrosianFlux > 0.0) ? extpars->petrosianFlux / extpars->petrosianFluxErr : NAN; // XXX zero point
+                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG",        PS_DATA_F32, "Petrosian Magnitude", mag);
+                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG_ERR",    PS_DATA_F32, "Petrosian Magnitude Error", magErr);
+                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS",     PS_DATA_F32, "Petrosian Radius (pix)", extpars->petrosianRadius);
+                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_ERR", PS_DATA_F32, "Petrosian Radius Error (pix)", extpars->petrosianRadiusErr);
+                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_50",     PS_DATA_F32, "Petrosian R50 (pix)", extpars->petrosianR50);
+                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_50_ERR", PS_DATA_F32, "Petrosian R50 Error (pix)", extpars->petrosianR50Err);
+                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_90",     PS_DATA_F32, "Petrosian R90 (pix)", extpars->petrosianR90);
+                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_90_ERR", PS_DATA_F32, "Petrosian R90 Error (pix)", extpars->petrosianR90Err);
+            } else {
+                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG",        PS_DATA_F32, "Petrosian Magnitude",       NAN);
+                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG_ERR",    PS_DATA_F32, "Petrosian Magnitude Error", NAN);
+                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS",     PS_DATA_F32, "Petrosian Radius",          NAN);
+                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_ERR", PS_DATA_F32, "Petrosian Radius Error",    NAN);
+                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_50",     PS_DATA_F32, "Petrosian R50 (pix)", NAN);
+                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_50_ERR", PS_DATA_F32, "Petrosian R50 Error (pix)",NAN); 
+                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_90",     PS_DATA_F32, "Petrosian R90 (pix)", NAN);
+                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_90_ERR", PS_DATA_F32, "Petrosian R90 Error (pix)",NAN); 
+            }
+        }
+
+        // Flux Annuli (if we have extended source measurements, we have these.  only optionally save them)
+        if (doAnnuli) {
+	    psVector *radSB   = psVectorAlloc(radMin->n, PS_TYPE_F32);
+	    psVector *radFlux = psVectorAlloc(radMin->n, PS_TYPE_F32);
+	    psVector *radFill = psVectorAlloc(radMin->n, PS_TYPE_F32);
+	    psVectorInit (radSB, NAN);
+	    psVectorInit (radFlux, NAN);
+	    psVectorInit (radFill, NAN);
+	    if (!source->extpars) goto empty_annuli;
+	    if (!source->extpars->radProfile) goto empty_annuli;
+	    if (!source->extpars->radProfile->binSB) goto empty_annuli;
+	    psAssert (source->extpars->radProfile->binSum, "programming error");
+	    psAssert (source->extpars->radProfile->binFill, "programming error");
+	    psAssert (source->extpars->radProfile->binSB->n <= radFlux->n, "inconsistent vector lengths");
+	    psAssert (source->extpars->radProfile->binSum->n <= radFlux->n, "inconsistent vector lengths");
+	    psAssert (source->extpars->radProfile->binFill->n <= radFlux->n, "inconsistent vector lengths");
+
+	    // copy the data from fluxVal (which is not guaranteed to be the full length) to radFlux
+	    for (int j = 0; j < source->extpars->radProfile->binSB->n; j++) {
+		radSB->data.F32[j]   = source->extpars->radProfile->binSB->data.F32[j];
+		radFlux->data.F32[j] = source->extpars->radProfile->binSum->data.F32[j];
+		radFill->data.F32[j] = source->extpars->radProfile->binFill->data.F32[j];
+	    }
+
+	empty_annuli:
+	    psMetadataAdd (row, PS_LIST_TAIL, "PROF_SB", PS_DATA_VECTOR, "mean surface brightness annuli", radSB);
+	    psMetadataAdd (row, PS_LIST_TAIL, "PROF_FLUX", PS_DATA_VECTOR, "flux within annuli", radFlux);
+	    psMetadataAdd (row, PS_LIST_TAIL, "PROF_FILL", PS_DATA_VECTOR, "fill factor of annuli", radFill);
+	    psFree (radSB);
+	    psFree (radFlux);
+	    psFree (radFill);
+	}
+	if (nRow < 0) {
+	    nRow = row->list->n;
+	} else {
+	    psAssert (nRow == row->list->n, "inconsistent row lengths");
+	}
+	psArrayAdd (table, 100, row);
+	psFree (row);
+    }
+    
+    if (table->n == 0) {
+	if (!psFitsWriteBlank (fits, outhead, extname)) {
+	    psError(psErrorCodeLast(), false, "Unable to write empty sources file.");
+	    psFree(outhead);
+	    psFree(table);
+	    return false;
+	}
+	psFree (outhead);
+	psFree (table);
+	return true;
+    }
+    
+    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
+    if (!psFitsWriteTable (fits, outhead, table, extname)) {
+	psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
+	psFree (outhead);
+    psFree(table);
+    return false;
+    }
+    psFree (outhead);
+    psFree (table);
+    
+    return true;
+}
+
+// XXX this layout is still the same as PS1_DEV_1
+bool pmSourcesWrite_CMF_PS1_V3_XFIT (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname)
+{
+
+    psArray *table;
+    psMetadata *row;
+    psF32 *PAR, *dPAR;
+    psEllipseAxes axes;
+    psF32 xPos, yPos;
+    psF32 xErr, yErr;
+    char name[64];
+
+    // create a header to hold the output data
+    psMetadata *outhead = psMetadataAlloc ();
+
+    // write the links to the image header
+    psMetadataAddStr (outhead, PS_LIST_TAIL, "EXTNAME", PS_META_REPLACE, "xsrc table extension", extname);
+
+    // let's write these out in S/N order
+    sources = psArraySort (sources, pmSourceSortBySN);
+
+    // we are writing one row per model; we need to write out same number of columns for each row: find the max Nparams
+    int nParamMax = 0;
+    for (int i = 0; i < sources->n; i++) {
+        pmSource *source = sources->data[i];
+        if (source->modelFits == NULL) continue;
+        for (int j = 0; j < source->modelFits->n; j++) {
+            pmModel *model = source->modelFits->data[j];
+            assert (model);
+            nParamMax = PS_MAX (nParamMax, model->params->n);
+        }
+    }
+
+    table = psArrayAllocEmpty (sources->n);
+
+    // we write out all sources, regardless of quality.  the source flags tell us the state
+    for (int i = 0; i < sources->n; i++) {
+
+        pmSource *source = sources->data[i];
+
+        // XXX if no model fits are saved, write out modelEXT?
+        if (source->modelFits == NULL) continue;
+
+        // We have multiple sources : need to flag the one used to subtract the light (the 'best' model)
+        for (int j = 0; j < source->modelFits->n; j++) {
+
+            // choose the convolved EXT model, if available, otherwise the simple one
+            pmModel *model = source->modelFits->data[j];
+            assert (model);
+
+	    // skip models which were not actually fitted
+	    if (model->flags & PM_MODEL_STATUS_BADARGS) continue;
+
+            PAR = model->params->data.F32;
+            dPAR = model->dparams->data.F32;
+            xPos = PAR[PM_PAR_XPOS];
+            yPos = PAR[PM_PAR_YPOS];
+            xErr = dPAR[PM_PAR_XPOS];
+            yErr = dPAR[PM_PAR_YPOS];
+
+            axes = pmPSF_ModelToAxes (PAR, 20.0);
+
+            row = psMetadataAlloc ();
+
+            // XXX we are not writing out the mode (flags) or the type (psf, ext, etc)
+            psMetadataAddU32 (row, PS_LIST_TAIL, "IPP_IDET",         0, "IPP detection identifier index",             source->seq);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "X_EXT",            0, "EXT model x coordinate",                     xPos);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "Y_EXT",            0, "EXT model y coordinate",                     yPos);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "X_EXT_SIG",        0, "Sigma in EXT x coordinate",                  xErr);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "Y_EXT_SIG",        0, "Sigma in EXT y coordinate",                  yErr);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_INST_MAG",     0, "EXT fit instrumental magnitude",             model->mag);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_INST_MAG_SIG", 0, "Sigma of PSF instrumental magnitude",        model->magErr);
+
+            psMetadataAddF32 (row, PS_LIST_TAIL, "NPARAMS",          0, "number of model parameters",                 model->params->n);
+            psMetadataAddStr (row, PS_LIST_TAIL, "MODEL_TYPE",       0, "name of model",                              pmModelClassGetName (model->type));
+
+            // XXX these should be major and minor, not 'x' and 'y'
+            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_WIDTH_MAJ",    0, "EXT width in x coordinate",                  axes.major);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_WIDTH_MIN",    0, "EXT width in y coordinate",                  axes.minor);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_THETA",        0, "EXT orientation angle",                      axes.theta);
+
+            // write out the other generic parameters
+            for (int k = 0; k < nParamMax; k++) {
+                if (k == PM_PAR_I0) continue;
+                if (k == PM_PAR_SKY) continue;
+                if (k == PM_PAR_XPOS) continue;
+                if (k == PM_PAR_YPOS) continue;
+                if (k == PM_PAR_SXX) continue;
+                if (k == PM_PAR_SXY) continue;
+                if (k == PM_PAR_SYY) continue;
+
+                snprintf (name, 64, "EXT_PAR_%02d", k);
+
+                if (k < model->params->n) {
+                    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "", model->params->data.F32[k]);
+                } else {
+                    psMetadataAddF32 (row, PS_LIST_TAIL, name, PS_DATA_F32, "", NAN);
+                }
+            }
+
+            // XXX other parameters which may be set.
+            // XXX flag / value to define the model
+            // XXX write out the model type, fit status flags
+
+            psArrayAdd (table, 100, row);
+            psFree (row);
+        }
+    }
+
+    if (table->n == 0) {
+        if (!psFitsWriteBlank (fits, outhead, extname)) {
+            psError(psErrorCodeLast(), false, "Unable to write empty sources file.");
+            psFree(outhead);
+            psFree(table);
+            return false;
+        }
+        psFree (outhead);
+        psFree (table);
+        return true;
+    }
+
+    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
+    if (!psFitsWriteTable (fits, outhead, table, extname)) {
+        psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
+        psFree (outhead);
+        psFree(table);
+        return false;
+    }
+    psFree (outhead);
+    psFree (table);
+    return true;
+}
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_CMP.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_CMP.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_CMP.c	(revision 29060)
@@ -28,15 +28,24 @@
 #include "pmFPAfile.h"
 
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmGrowthCurve.h"
-#include "pmResiduals.h"
-#include "pmTrend2D.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
-#include "pmSource.h"
-#include "pmModelClass.h"
+#include "pmPSFtry.h"
+
 #include "pmSourceIO.h"
 
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_MatchedRefs.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_MatchedRefs.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_MatchedRefs.c	(revision 29060)
@@ -28,16 +28,26 @@
 #include "pmFPAfile.h"
 
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmGrowthCurve.h"
-#include "pmResiduals.h"
-#include "pmTrend2D.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
-#include "pmSource.h"
-#include "pmModelClass.h"
+#include "pmPSFtry.h"
+
 #include "pmSourceIO.h"
+
 #include "pmAstrometryObjects.h"
 #include "pmAstrometryWCS.h"
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_OBJ.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_OBJ.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_OBJ.c	(revision 29060)
@@ -28,15 +28,24 @@
 #include "pmFPAfile.h"
 
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmGrowthCurve.h"
-#include "pmResiduals.h"
-#include "pmTrend2D.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
-#include "pmSource.h"
-#include "pmModelClass.h"
+#include "pmPSFtry.h"
+
 #include "pmSourceIO.h"
 
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_PS1_CAL_0.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_PS1_CAL_0.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_PS1_CAL_0.c	(revision 29060)
@@ -28,15 +28,24 @@
 #include "pmFPAfile.h"
 
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmGrowthCurve.h"
-#include "pmResiduals.h"
-#include "pmTrend2D.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
-#include "pmSource.h"
-#include "pmModelClass.h"
+#include "pmPSFtry.h"
+
 #include "pmSourceIO.h"
 
@@ -49,6 +58,5 @@
 // XXX how do I generate the source tables which I need to send to PSPS?
 
-bool pmSourcesWrite_PS1_CAL_0 (psFits *fits, pmReadout *readout, psArray *sources, 
-			       psMetadata *imageHeader, psMetadata *tableHeader, char *extname)
+bool pmSourcesWrite_PS1_CAL_0 (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe)
 {
     PS_ASSERT_PTR_NON_NULL(fits, false);
@@ -180,5 +188,5 @@
         psMetadataAdd (row, PS_LIST_TAIL, "PSF_WIDTH_Y",      PS_DATA_F32, "PSF width in y coordinate",                  axes.minor);
         psMetadataAdd (row, PS_LIST_TAIL, "PSF_THETA",        PS_DATA_F32, "PSF orientation angle",                      axes.theta);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF",           PS_DATA_F32, "PSF coverage/quality factor",                source->pixWeight);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF",           PS_DATA_F32, "PSF coverage/quality factor",                source->pixWeightNotBad);
 
         // XXX not sure how to get this : need to load Nimages with weight?
@@ -289,6 +297,8 @@
         source->peak->dx   = dPAR[PM_PAR_XPOS];
         source->peak->dy   = dPAR[PM_PAR_YPOS];
-
-        source->pixWeight = psMetadataLookupF32 (&status, row, "PSF_QF");
+        source->peak->xf   = PAR[PM_PAR_XPOS]; // more accurate position
+        source->peak->yf   = PAR[PM_PAR_YPOS]; // more accurate position
+
+        source->pixWeightNotBad = psMetadataLookupF32 (&status, row, "PSF_QF");
 
 	// note that some older versions used PSF_PROBABILITY: this was not well defined.
@@ -597,4 +607,7 @@
 	    assert (model);
 
+	    // skip models which were not actually fitted
+	    if (model->flags & PM_MODEL_STATUS_BADARGS) continue;
+
 	    PAR = model->params->data.F32;
 	    dPAR = model->dparams->data.F32;
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_PS1_DEV_0.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_PS1_DEV_0.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_PS1_DEV_0.c	(revision 29060)
@@ -28,15 +28,24 @@
 #include "pmFPAfile.h"
 
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmGrowthCurve.h"
-#include "pmResiduals.h"
-#include "pmTrend2D.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
-#include "pmSource.h"
-#include "pmModelClass.h"
+#include "pmPSFtry.h"
+
 #include "pmSourceIO.h"
 
@@ -49,6 +58,5 @@
 // XXX how do I generate the source tables which I need to send to PSPS?
 // XXX: input parameter imageHeader is never used.
-bool pmSourcesWrite_PS1_DEV_0 (psFits *fits, psArray *sources, psMetadata *imageHeader,
-                               psMetadata *tableHeader, char *extname)
+bool pmSourcesWrite_PS1_DEV_0 (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe)
 {
     PS_ASSERT_PTR_NON_NULL(fits, false);
@@ -117,5 +125,5 @@
         psMetadataAdd (row, PS_LIST_TAIL, "PSF_WIDTH_Y",      PS_DATA_F32, "PSF width in y coordinate",                  axes.minor);
         psMetadataAdd (row, PS_LIST_TAIL, "PSF_THETA",        PS_DATA_F32, "PSF orientation angle",                      axes.theta);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF",           PS_DATA_F32, "PSF coverage/quality factor",                source->pixWeight);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF",           PS_DATA_F32, "PSF coverage/quality factor",                source->pixWeightNotBad);
         // XXX not sure how to get this : need to load Nimages with weight
         psMetadataAdd (row, PS_LIST_TAIL, "N_FRAMES",         PS_DATA_U16, "Number of frames overlapping source center", nImageOverlap);
@@ -215,6 +223,8 @@
         source->peak->dx   = dPAR[PM_PAR_XPOS];
         source->peak->dy   = dPAR[PM_PAR_YPOS];
-
-        source->pixWeight = psMetadataLookupF32 (&status, row, "PSF_QF");
+        source->peak->xf   = PAR[PM_PAR_XPOS]; // more accurate position
+        source->peak->yf   = PAR[PM_PAR_YPOS]; // more accurate position
+
+        source->pixWeightNotBad = psMetadataLookupF32 (&status, row, "PSF_QF");
 
         // XXX other values saved but not loaded?
@@ -228,2 +238,12 @@
     return (sources);
 }
+
+bool pmSourcesWrite_PS1_DEV_0_XSRC (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe)
+{
+    return true;
+}
+
+bool pmSourcesWrite_PS1_DEV_0_XFIT(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname)
+{
+    return true;
+}
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_PS1_DEV_1.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_PS1_DEV_1.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_PS1_DEV_1.c	(revision 29060)
@@ -28,15 +28,24 @@
 #include "pmFPAfile.h"
 
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmGrowthCurve.h"
-#include "pmResiduals.h"
-#include "pmTrend2D.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
-#include "pmSource.h"
-#include "pmModelClass.h"
+#include "pmPSFtry.h"
+
 #include "pmSourceIO.h"
 
@@ -47,8 +56,6 @@
 // this output format is valid for psphot analysis of an image, and does not include calibrated
 // values derived in the DVO database.
-// XXX how do I generate the source tables which I need to send to PSPS?
-
-bool pmSourcesWrite_PS1_DEV_1 (psFits *fits, psArray *sources, 
-			       psMetadata *imageHeader, psMetadata *tableHeader, char *extname)
+
+bool pmSourcesWrite_PS1_DEV_1(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe)
 {
     PS_ASSERT_PTR_NON_NULL(fits, false);
@@ -142,5 +149,5 @@
         psMetadataAdd (row, PS_LIST_TAIL, "PSF_WIDTH_Y",      PS_DATA_F32, "PSF width in y coordinate",                  axes.minor);
         psMetadataAdd (row, PS_LIST_TAIL, "PSF_THETA",        PS_DATA_F32, "PSF orientation angle",                      axes.theta);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF",           PS_DATA_F32, "PSF coverage/quality factor",                source->pixWeight);
+        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF",           PS_DATA_F32, "PSF coverage/quality factor",                source->pixWeightNotBad);
 
         // XXX not sure how to get this : need to load Nimages with weight?
@@ -259,6 +266,8 @@
         source->peak->dx   = dPAR[PM_PAR_XPOS];
         source->peak->dy   = dPAR[PM_PAR_YPOS];
-
-        source->pixWeight = psMetadataLookupF32 (&status, row, "PSF_QF");
+        source->peak->xf   = PAR[PM_PAR_XPOS]; // more accurate position
+        source->peak->yf   = PAR[PM_PAR_YPOS]; // more accurate position
+
+        source->pixWeightNotBad = psMetadataLookupF32 (&status, row, "PSF_QF");
 
 	// note that some older versions used PSF_PROBABILITY: this was not well defined.
@@ -281,5 +290,5 @@
 }
 
-bool pmSourcesWrite_PS1_DEV_1_XSRC (psFits *fits, psArray *sources, char *extname, psMetadata *recipe)
+bool pmSourcesWrite_PS1_DEV_1_XSRC (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe)
 {
 
@@ -453,5 +462,5 @@
 }
 
-bool pmSourcesWrite_PS1_DEV_1_XFIT (psFits *fits, psArray *sources, char *extname)
+bool pmSourcesWrite_PS1_DEV_1_XFIT(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname)
 {
 
@@ -502,4 +511,7 @@
 	    assert (model);
 
+	    // skip models which were not actually fitted
+	    if (model->flags & PM_MODEL_STATUS_BADARGS) continue;
+
 	    PAR = model->params->data.F32;
 	    dPAR = model->dparams->data.F32;
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_RAW.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_RAW.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_RAW.c	(revision 29060)
@@ -28,16 +28,24 @@
 #include "pmFPAfile.h"
 
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmGrowthCurve.h"
-#include "pmResiduals.h"
-#include "pmTrend2D.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
-#include "pmSource.h"
-#include "pmModelClass.h"
-#include "pmSourcePhotometry.h"
+#include "pmPSFtry.h"
+
 #include "pmSourceIO.h"
 
@@ -120,5 +128,5 @@
                  source[0].peak->SN,
                  source[0].apRadius,
-                 source[0].pixWeight,
+                 source[0].pixWeightNotBad,
                  model[0].nDOF,
                  model[0].nIter);
@@ -182,5 +190,5 @@
                  source[0].peak->SN,
                  source[0].apRadius,
-                 source[0].pixWeight,
+                 source[0].pixWeightNotBad,
                  model[0].nDOF,
                  model[0].nIter);
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_SMPDATA.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_SMPDATA.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_SMPDATA.c	(revision 29060)
@@ -28,15 +28,24 @@
 #include "pmFPAfile.h"
 
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmResiduals.h"
-#include "pmGrowthCurve.h"
-#include "pmTrend2D.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
-#include "pmSource.h"
-#include "pmModelClass.h"
+#include "pmPSFtry.h"
+
 #include "pmSourceIO.h"
 
@@ -45,6 +54,5 @@
 // followed by a zero-size matrix, followed by the table data
 // XXX: input parameter imageHeader is never used
-bool pmSourcesWrite_SMPDATA (psFits *fits, psArray *sources, psMetadata *imageHeader,
-                             psMetadata *tableHeader, char *extname)
+bool pmSourcesWrite_SMPDATA (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe)
 {
     PS_ASSERT_PTR_NON_NULL(fits, false);
@@ -106,5 +114,5 @@
         psMetadataAdd (row, PS_LIST_TAIL, "THETA",   PS_DATA_F32, "", axes.theta);
         psMetadataAdd (row, PS_LIST_TAIL, "DOPHOT",  PS_DATA_U8,  "", type);
-        psMetadataAdd (row, PS_LIST_TAIL, "WEIGHT",  PS_DATA_U8,  "", PS_MIN (255, PS_MAX(0, 255*source->pixWeight)));
+        psMetadataAdd (row, PS_LIST_TAIL, "WEIGHT",  PS_DATA_U8,  "", PS_MIN (255, PS_MAX(0, 255*source->pixWeightNotBad)));
         psMetadataAdd (row, PS_LIST_TAIL, "DUMMY",   PS_DATA_U16, "", 0);
 
@@ -189,5 +197,5 @@
         source->apMag  = psMetadataLookupF32 (&status, row, "MAG_AP")  - ZERO_POINT;
 
-        source->pixWeight = psMetadataLookupU8 (&status, row, "WEIGHT")/255.0;
+        source->pixWeightNotBad = psMetadataLookupU8 (&status, row, "WEIGHT")/255.0;
         int dophot = psMetadataLookupU8 (&status, row, "DOPHOT");
 	pmSourceSetDophotType (source, dophot);
@@ -204,2 +212,12 @@
     return (sources);
 }
+
+bool pmSourcesWrite_SMPDATA_XSRC (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe)
+{
+    return true;
+}
+
+bool pmSourcesWrite_SMPDATA_XFIT(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname)
+{
+    return true;
+} 
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_SX.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_SX.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceIO_SX.c	(revision 29060)
@@ -28,15 +28,24 @@
 #include "pmFPAfile.h"
 
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmGrowthCurve.h"
-#include "pmResiduals.h"
-#include "pmTrend2D.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
-#include "pmSource.h"
-#include "pmModelClass.h"
+#include "pmPSFtry.h"
+
 #include "pmSourceIO.h"
 
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceMasks.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceMasks.h	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceMasks.h	(revision 29060)
@@ -39,4 +39,11 @@
 } pmSourceMode;
 
+// Bit flags to distinguish analysis results
+// When adding to or subtracting from this list, please also modify pmSourceMaskHeader
+typedef enum {
+    PM_SOURCE_MODE2_DEFAULT          = 0x00000000, ///< Initial value: resets all bits
+    PM_SOURCE_MODE2_DIFF_WITH_SINGLE = 0x00000001, ///< diff source matched to a single positive detection
+    PM_SOURCE_MODE2_DIFF_WITH_DOUBLE = 0x00000002, ///< diff source matched to positive detections in both images
+} pmSourceMode2;
 
 /// Populate header with mask values
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceMatch.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceMatch.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceMatch.c	(revision 29060)
@@ -6,6 +6,28 @@
 #include <string.h>
 #include <pslib.h>
-
+#include "pmHDU.h"
+#include "pmFPA.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmSpan.h"
+#include "pmFootprintSpans.h"
+#include "pmFootprint.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
 #include "pmSource.h"
+#include "pmSourceFitModel.h"
+#include "pmPSF.h"
+#include "pmPSFtry.h"
+#include "pmDetections.h"
+
 #include "pmErrorCodes.h"
 
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceMatch.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceMatch.h	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceMatch.h	(revision 29060)
@@ -1,6 +1,4 @@
 #ifndef PM_SOURCE_MATCH_H
 #define PM_SOURCE_MATCH_H
-
-#include <pslib.h>
 
 /// Mask values for matched sources
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceMoments.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceMoments.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceMoments.c	(revision 29060)
@@ -22,16 +22,23 @@
 #include <strings.h>
 #include <pslib.h>
+
 #include "pmHDU.h"
 #include "pmFPA.h"
-#include "pmFPAMaskWeight.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmResiduals.h"
-#include "pmGrowthCurve.h"
-#include "pmTrend2D.h"
-#include "pmPSF.h"
+#include "pmModelFuncs.h"
 #include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
 #include "pmSource.h"
 
@@ -54,4 +61,7 @@
 # define VALID_RADIUS(X,Y,RAD2) (((RAD2) >= (PS_SQR(X) + PS_SQR(Y))) ? 1 : 0)
 
+static bool beVerbose = false;
+void pmSourceMomentsSetVerbose(bool state){ beVerbose = state; }
+
 bool pmSourceMoments(pmSource *source, psF32 radius, psF32 sigma, psF32 minSN, psImageMaskType maskVal)
 {
@@ -61,11 +71,18 @@
     PS_ASSERT_FLOAT_LARGER_THAN(radius, 0.0, false);
 
-    // use sky from moments if defined, 0.0 otherwise
+    // use sky from moments if defined, 0.0 otherwise 
+
+    // XXX this value comes from the sky model at the source center, and tends to over-estimate
+    // the sky in the vicinity of bright sources.  we are better off assuming the model worked
+    // well:
     psF32 sky = 0.0;
     if (source->moments == NULL) {
-        source->moments = pmMomentsAlloc();
-    } else {
-        sky = source->moments->Sky;
-    }
+      source->moments = pmMomentsAlloc();
+    }
+    // XXX if (source->moments == NULL) {
+    // XXX     source->moments = pmMomentsAlloc();
+    // XXX } else {
+    // XXX     sky = source->moments->Sky;
+    // XXX }
 
     // First Pass: calculate the first moments (these are subtracted from the coordinates below)
@@ -131,9 +148,11 @@
             psF32 wDiff = *vWgt;
 
-	    // skip pixels below specified significance level.  this is allowed, but should be
-	    // avoided -- the over-weights the wings of bright stars compared to those of faint
-	    // stars.
-            if (PS_SQR(pDiff) < minSN2*wDiff) continue;
-            // if (pDiff < 0) continue; // XXX : MWV says I should include < 0.0 valued points...
+	    // skip pixels below specified significance level.  for a PSFs, this
+	    // over-weights the wings of bright stars compared to those of faint stars.
+	    // for the estimator used for extended source analysis (where the window
+	    // function is allowed to be arbitrarily large), we need to clip to avoid
+	    // negative second moments.
+            if (PS_SQR(pDiff) < minSN2*wDiff) continue; // 
+            if ((minSN > 0.0) && (pDiff < 0)) continue; // 
 
 	    // Apply a Gaussian window function.  Be careful with the window function.  S/N
@@ -197,4 +216,7 @@
     // Xn  = SUM (x - xc)^n * (z - sky)
 
+    psF32 RF = 0.0;
+    psF32 RH = 0.0;
+    psF32 RS = 0.0;
     psF32 XX = 0.0;
     psF32 XY = 0.0;
@@ -244,5 +266,6 @@
 	    if (r > radius) continue;
 
-	    psF32 pDiff = *vPix - sky;
+	    psF32 fDiff = *vPix - sky;
+	    psF32 pDiff = fDiff;
 	    psF32 wDiff = *vWgt;
 
@@ -257,5 +280,7 @@
 	    if (sigma > 0.0) {
 		// XXX a lot of extra flops; can we do pre-calculate?
-		psF32 z  = (PS_SQR(xDiff) + PS_SQR(yDiff))*rsigma2;
+		// XXX we were re-calculating r2 (maybe the compiler caught this?)
+		// psF32 z  = (PS_SQR(xDiff) + PS_SQR(yDiff))*rsigma2;
+		psF32 z  = r2 * rsigma2;
 		assert (z >= 0.0);
 		psF32 weight  = exp(-z);
@@ -266,4 +291,17 @@
 
 	    Sum += pDiff;
+
+# if (1)
+# if (0)
+	    if (fDiff < 0) continue;
+# endif
+	    psF32 rf = r * fDiff;
+	    psF32 rh = sqrt(r) * fDiff;
+	    psF32 rs = fDiff;
+# else
+	    psF32 rf = r * pDiff;
+	    psF32 rh = sqrt(r) * pDiff;
+	    psF32 rs = pDiff;
+# endif
 
 	    psF32 x = xDiff * pDiff;
@@ -284,4 +322,8 @@
 	    psF32 xyyy = xDiff * yyy / r2;
 	    psF32 yyyy = yDiff * yyy / r2;
+
+	    RF  += rf;
+	    RH  += rh;
+	    RS  += rs;
 
 	    XX  += xx;
@@ -302,4 +344,7 @@
     }
 
+    source->moments->Mrf = RF/RS;
+    source->moments->Mrh = RH/RS;
+
     source->moments->Mxx = XX/Sum;
     source->moments->Mxy = XY/Sum;
@@ -317,12 +362,71 @@
     source->moments->Myyyy = YYYY/Sum;
 
-    // if (source->moments->Mxx < 0) {
-    // 	fprintf (stderr, "error: neg second moment??\n");
-    // }
-    // if (source->moments->Myy < 0) {
-    // 	fprintf (stderr, "error: neg second moment??\n");
-    // }
-
-    psTrace ("psModules.objects", 4, "Mxx: %f  Mxy: %f  Myy: %f  Mxxx: %f  Mxxy: %f  Mxyy: %f  Myyy: %f  Mxxxx: %f  Mxxxy: %f  Mxxyy: %f  Mxyyy: %f  Mxyyy: %f\n",
+    // Calculate the Kron magnitude (make this block optional?)
+    // float radKron = 2.5*source->moments->Mrf;
+    float radKinner = 1.0*source->moments->Mrf;
+    float radKron   = 2.5*source->moments->Mrf;
+    float radKouter = 4.0*source->moments->Mrf;
+
+    int nKronPix = 0;
+    Sum = Var = 0.0;
+    float SumInner = 0.0;
+    float SumOuter = 0.0;
+
+    for (psS32 row = 0; row < source->pixels->numRows ; row++) {
+
+	psF32 yDiff = row - yCM;
+	if (fabs(yDiff) > radKron) continue;
+
+	psF32 *vPix = source->pixels->data.F32[row];
+	psF32 *vWgt = source->variance->data.F32[row];
+	psImageMaskType  *vMsk = (source->maskObj == NULL) ? NULL : source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[row];
+
+	for (psS32 col = 0; col < source->pixels->numCols ; col++, vPix++, vWgt++) {
+	    if (vMsk) {
+		if (*vMsk & maskVal) {
+		    vMsk++;
+		    continue;
+		}
+		vMsk++;
+	    }
+	    if (isnan(*vPix)) continue;
+
+	    psF32 xDiff = col - xCM;
+	    if (fabs(xDiff) > radKron) continue;
+
+	    // radKron is just a function of (xDiff, yDiff)
+	    psF32 r2  = PS_SQR(xDiff) + PS_SQR(yDiff);
+	    psF32 r  = sqrt(r2);
+
+	    psF32 pDiff = *vPix - sky;
+	    psF32 wDiff = *vWgt;
+
+	    // skip pixels below specified significance level.  this is allowed, but should be
+	    // avoided -- the over-weights the wings of bright stars compared to those of faint
+	    // stars.
+	    if (PS_SQR(pDiff) < minSN2*wDiff) continue;
+
+	    if (r < radKron) {
+		Sum += pDiff;
+		Var += wDiff;
+		nKronPix ++;
+		// if (beVerbose) fprintf (stderr, "mome: %d %d  %f  %f  %f\n", col, row, sky, *vPix, Sum);
+	    }
+
+	    if ((r > radKinner) && (r < radKron)) {
+		SumInner += pDiff;
+	    }
+	    if ((r > radKron)  && (r < radKouter)) {
+		SumOuter += pDiff;
+	    }
+	}
+    }
+    source->moments->KronFlux = Sum;
+    source->moments->KronFinner = SumInner;
+    source->moments->KronFouter = SumOuter;
+    source->moments->KronFluxErr = sqrt(Var);
+
+    psTrace ("psModules.objects", 4, "Mrf: %f  KronFlux: %f  Mxx: %f  Mxy: %f  Myy: %f  Mxxx: %f  Mxxy: %f  Mxyy: %f  Myyy: %f  Mxxxx: %f  Mxxxy: %f  Mxxyy: %f  Mxyyy: %f  Mxyyy: %f\n",
+	     source->moments->Mrf,   source->moments->KronFlux, 
 	     source->moments->Mxx,   source->moments->Mxy,   source->moments->Myy,
 	     source->moments->Mxxx,  source->moments->Mxxy,  source->moments->Mxyy,  source->moments->Myyy,
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourcePhotometry.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourcePhotometry.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourcePhotometry.c	(revision 29060)
@@ -22,15 +22,25 @@
 #include "pmFPA.h"
 #include "pmFPAMaskWeight.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmGrowthCurve.h"
-#include "pmResiduals.h"
-#include "pmTrend2D.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
-#include "pmSource.h"
-#include "pmModelClass.h"
+#include "pmPSFtry.h"
+
 #include "pmSourcePhotometry.h"
 
@@ -66,5 +76,5 @@
 
 // XXX masked region should be (optionally) elliptical
-bool pmSourceMagnitudes (pmSource *source, pmPSF *psf, pmSourcePhotometryMode mode, psImageMaskType maskVal)
+bool pmSourceMagnitudes (pmSource *source, pmPSF *psf, pmSourcePhotometryMode mode, psImageMaskType maskVal, psImageMaskType markVal)
 {
     PS_ASSERT_PTR_NON_NULL(source, false);
@@ -122,4 +132,5 @@
         for (int i = 0; i < source->modelFits->n; i++) {
             pmModel *model = source->modelFits->data[i];
+	    if (model->flags & PM_MODEL_STATUS_BADARGS) continue;
             status = pmSourcePhotometryModel (&model->mag, NULL, model);
             if (model == source->modelEXT) foundEXT = true;
@@ -145,10 +156,10 @@
     // measure the contribution of included pixels
     if (mode & PM_SOURCE_PHOT_WEIGHT) {
-        pmSourcePixelWeight (&source->pixWeight, model, source->maskObj, maskVal);
+        pmSourcePixelWeight (&source->pixWeightNotBad, &source->pixWeightNotPoor, model, source->maskObj, maskVal, markVal);
     }
 
     // measure the contribution of included pixels
     if (mode & PM_SOURCE_PHOT_DIFFSTATS) {
-        pmSourceMeasureDiffStats (source, maskVal);
+        pmSourceMeasureDiffStats (source, maskVal, markVal);
     }
 
@@ -191,5 +202,5 @@
 
     // measure object aperture photometry
-    status = pmSourcePhotometryAper  (&source->apMag, model, flux, mask, maskVal);
+    status = pmSourcePhotometryAper  (&source->apMagRaw, model, flux, mask, maskVal);
     if (!status) {
         psTrace ("psModules.objects", 3, "fail mag : bad Ap Mag");
@@ -199,11 +210,15 @@
     // if the aper mag is NAN, the flux < 0.  this can happen for sources near the
     // detection limits (esp near bright neighbors)
+    source->apMag = source->apMagRaw;
     if (isfinite (source->apMag) && isPSF && psf) {
         if (psf->growth && (mode & PM_SOURCE_PHOT_GROWTH)) {
-            source->apMag += pmGrowthCurveCorrect (psf->growth, source->apRadius);
+            source->apMag = source->apMagRaw + pmGrowthCurveCorrect (psf->growth, source->apRadius);
         }
         if (mode & PM_SOURCE_PHOT_APCORR) {
             // XXX this should be removed -- we no longer fit for the 'sky bias'
+	    // XXX is this happening???
             rflux   = pow (10.0, 0.4*source->psfMag);
+	    psAssert (psf->skyBias == 0.0, "sky bias not 0");
+	    psAssert (psf->skySat == 0.0, "sky sat not 0");
             source->apMag -= PS_SQR(source->apRadius)*rflux * psf->skyBias + psf->skySat / rflux;
         }
@@ -257,5 +272,8 @@
     PS_ASSERT_PTR_NON_NULL(image, false);
     PS_ASSERT_PTR_NON_NULL(mask, false);
-    PS_ASSERT_PTR_NON_NULL(model, false);
+
+    if (DO_SKY) {
+	PS_ASSERT_PTR_NON_NULL(model, false);
+    }
 
     float apSum = 0;
@@ -271,11 +289,14 @@
     psF32 **imData = image->data.F32;
     psImageMaskType **mkData = mask->data.PS_TYPE_IMAGE_MASK_DATA;
+    int nAperPix = 0;
 
     // measure apMag
-    for (int ix = 0; ix < image->numCols; ix++) {
-        for (int iy = 0; iy < image->numRows; iy++) {
+    for (int iy = 0; iy < image->numRows; iy++) {
+	for (int ix = 0; ix < image->numCols; ix++) {
             if (mkData[iy][ix] & maskVal)
                 continue;
             apSum += imData[iy][ix] - sky;
+	    nAperPix ++;
+	    // fprintf (stderr, "aper: %d %d  %f  %f  %f\n", ix, iy, sky, imData[iy][ix], apSum);
         }
     }
@@ -290,12 +311,14 @@
 
 // return source aperture magnitude
-bool pmSourcePixelWeight (float *pixWeight, pmModel *model, psImage *mask, psImageMaskType maskVal)
-{
-    PS_ASSERT_PTR_NON_NULL(pixWeight, false);
+bool pmSourcePixelWeight (float *pixWeightNotBad, float *pixWeightNotPoor, pmModel *model, psImage *mask, psImageMaskType maskVal, psImageMaskType markVal)
+{
+    PS_ASSERT_PTR_NON_NULL(pixWeightNotBad, false);
+    PS_ASSERT_PTR_NON_NULL(pixWeightNotPoor, false);
     PS_ASSERT_PTR_NON_NULL(mask, false);
     PS_ASSERT_PTR_NON_NULL(model, false);
 
     float modelSum = 0;
-    float validSum = 0;
+    float notBadSum = 0;
+    float notPoorSum = 0;
     float sky = 0;
     float value;
@@ -305,5 +328,6 @@
     int dY, DY, NY;
 
-    *pixWeight = 0.0;
+    *pixWeightNotBad = 0.0;
+    *pixWeightNotPoor = 0.0;
 
     // we only care about the value of the object model, not the local sky
@@ -345,28 +369,26 @@
 
             // for the full model, add all points
-            value = model->modelFunc (NULL, params, coord) - sky;
+            value = fabs(model->modelFunc (NULL, params, coord) - sky);
             modelSum += value;
 
             // include count only the unmasked pixels within the image area
-            if (mx < 0)
-                continue;
-            if (my < 0)
-                continue;
-            if (mx >= NX)
-                continue;
-            if (my >= NY)
-                continue;
-            if (mask->data.PS_TYPE_IMAGE_MASK_DATA[my][mx] & maskVal)
-                continue;
-
-            validSum += value;
+            if (mx < 0) continue;
+            if (my < 0) continue;
+            if (mx >= NX) continue;
+            if (my >= NY) continue;
+
+            if (!(mask->data.PS_TYPE_IMAGE_MASK_DATA[my][mx] & maskVal)) {
+		notBadSum += value;
+	    }
+            if (!(mask->data.PS_TYPE_IMAGE_MASK_DATA[my][mx] & ~markVal)) {
+		notPoorSum += value;
+	    }
         }
     }
     psFree (coord);
 
-    if (validSum <= 0)
-        return false;
-
-    *pixWeight = validSum / modelSum;
+    *pixWeightNotBad  = notBadSum  / modelSum;
+    *pixWeightNotPoor = notPoorSum / modelSum;
+
     return (true);
 }
@@ -374,6 +396,6 @@
 # define FLUX_LIMIT 3.0
 
-// return source aperture magnitude
-bool pmSourceMeasureDiffStats (pmSource *source, psImageMaskType maskVal)
+// measure stats that may be using in difference images for distinguishing real sources from bad residuals
+bool pmSourceMeasureDiffStats (pmSource *source, psImageMaskType maskVal, psImageMaskType markVal)
 {
     PS_ASSERT_PTR_NON_NULL(source, false);
@@ -399,4 +421,8 @@
     for (int iy = 0; iy < flux->numRows; iy++) {
         for (int ix = 0; ix < flux->numCols; ix++) {
+	    // only count up the stats in the unmarked region (ie, the aperture)
+            if (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] & markVal) {
+                continue;
+            }
             if (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] & maskVal) {
                 nMask ++;
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourcePhotometry.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourcePhotometry.h	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourcePhotometry.h	(revision 29060)
@@ -52,9 +52,11 @@
 
 bool pmSourceMagnitudesInit (psMetadata *config);
-bool pmSourceMagnitudes (pmSource *source, pmPSF *psf, pmSourcePhotometryMode mode, psImageMaskType maskVal);
-bool pmSourcePixelWeight (float *pixWeight, pmModel *model, psImage *mask, psImageMaskType maskVal);
+bool pmSourceMagnitudes (pmSource *source, pmPSF *psf, pmSourcePhotometryMode mode, psImageMaskType maskVal, psImageMaskType markVal);
+
+bool pmSourcePixelWeight (float *pixWeightNotBad, float *pixWeightNotPoor, pmModel *model, psImage *mask, psImageMaskType maskVal, psImageMaskType markVal);
+
 bool pmSourceChisq (pmModel *model, psImage *image, psImage *mask, psImage *weight, psImageMaskType maskVal, const float covarFactor);
 
-bool pmSourceMeasureDiffStats (pmSource *source, psImageMaskType maskVal);
+bool pmSourceMeasureDiffStats (pmSource *source, psImageMaskType maskVal, psImageMaskType markVal);
 
 double pmSourceDataDotModel (const pmSource *Mi, const pmSource *Mj, const bool unweighted_sum, const float covarFactor, psImageMaskType maskVal);
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourcePlotApResid.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourcePlotApResid.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourcePlotApResid.c	(revision 29060)
@@ -25,15 +25,25 @@
 #include "pmFPAview.h"
 #include "pmFPAfile.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmResiduals.h"
-#include "pmGrowthCurve.h"
-#include "pmTrend2D.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
 #include "pmDetections.h"
-#include "pmSource.h"
+
 #include "pmSourcePlots.h"
 #include "pmKapaPlots.h"
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourcePlotMoments.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourcePlotMoments.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourcePlotMoments.c	(revision 29060)
@@ -28,15 +28,24 @@
 #include "pmFPAview.h"
 #include "pmFPAfile.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmGrowthCurve.h"
-#include "pmResiduals.h"
-#include "pmTrend2D.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
 #include "pmDetections.h"
-#include "pmSource.h"
 #include "pmSourcePlots.h"
 #include "pmKapaPlots.h"
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourcePlotPSFModel.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourcePlotPSFModel.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourcePlotPSFModel.c	(revision 29060)
@@ -28,15 +28,25 @@
 #include "pmFPAview.h"
 #include "pmFPAfile.h"
+
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmGrowthCurve.h"
-#include "pmResiduals.h"
-#include "pmTrend2D.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
-#include "pmModel.h"
 #include "pmDetections.h"
-#include "pmSource.h"
 #include "pmSourcePlots.h"
 #include "pmKapaPlots.h"
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceSky.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceSky.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceSky.c	(revision 29060)
@@ -21,17 +21,25 @@
 #include <string.h>
 #include <pslib.h>
+
 #include "pmHDU.h"
 #include "pmFPA.h"
-#include "pmFPAMaskWeight.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmResiduals.h"
-#include "pmGrowthCurve.h"
-#include "pmTrend2D.h"
-#include "pmPSF.h"
+#include "pmModelFuncs.h"
 #include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
 #include "pmSource.h"
+
 #include "pmSourceSky.h"
 
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceUtils.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceUtils.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceUtils.c	(revision 29060)
@@ -21,17 +21,25 @@
 #include <string.h>
 #include <pslib.h>
+
 #include "pmHDU.h"
 #include "pmFPA.h"
-#include "pmFPAMaskWeight.h"
+
+#include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
 #include "pmSpan.h"
+#include "pmFootprintSpans.h"
 #include "pmFootprint.h"
 #include "pmPeaks.h"
 #include "pmMoments.h"
-#include "pmResiduals.h"
-#include "pmGrowthCurve.h"
-#include "pmTrend2D.h"
-#include "pmPSF.h"
+#include "pmModelFuncs.h"
 #include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
 #include "pmSource.h"
+
 #include "pmSourceUtils.h"
 
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSourceVisual.c
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSourceVisual.c	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSourceVisual.c	(revision 29060)
@@ -4,8 +4,28 @@
 
 #include <pslib.h>
+#include "pmHDU.h"
+#include "pmFPA.h"
+
 #include "pmTrend2D.h"
+#include "pmResiduals.h"
+#include "pmGrowthCurve.h"
+#include "pmSpan.h"
+#include "pmFootprintSpans.h"
+#include "pmFootprint.h"
+#include "pmPeaks.h"
+#include "pmMoments.h"
+#include "pmModelFuncs.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmModelClass.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
 #include "pmPSF.h"
 #include "pmPSFtry.h"
-#include "pmSource.h"
+#include "pmDetections.h"
+
 #include "pmSourceVisual.h"
 
@@ -13,4 +33,5 @@
 #include <kapa.h>
 #include "pmVisual.h"
+#include "pmVisualUtils.h"
 
 // functions used to visualize the analysis as it goes
@@ -34,5 +55,5 @@
     Graphdata graphdata;
 
-    if (!pmVisualIsVisual()) return true;
+    if (!pmVisualTestLevel("psphot.psf.metric", 2)) return true;
 
     if (kapa1 == -1) {
@@ -118,5 +139,5 @@
     Graphdata graphdata;
 
-    if (!pmVisualIsVisual()) return true;
+    if (!pmVisualTestLevel("psphot.psf.subpix", 3)) return true;
 
     if (kapa1 == -1) {
@@ -280,5 +301,5 @@
 bool pmSourceVisualShowModelFits (pmPSF *psf, psArray *sources, psImageMaskType maskVal) {
 
-    if (!pmVisualIsVisual()) return true;
+    if (!pmVisualTestLevel("psphot.psf.fits", 2)) return true;
 
     if (kapa2 == -1) {
@@ -360,5 +381,6 @@
 bool pmSourceVisualShowModelFit (pmSource *source) {
 
-    if (!pmVisualIsVisual()) return true;
+    if (!pmVisualTestLevel("psphot.psf.fitresid", 2)) return true;
+
     if (!source->pixels) return false;
     if (!source->modelFlux) return false;
@@ -404,5 +426,6 @@
     Graphdata graphdata;
 
-    if (!pmVisualIsVisual() || !plotPSF) return true;
+    if (!plotPSF) return true;
+    if (!pmVisualTestLevel("psphot.psf.resid", 2)) return true;
 
     if (kapa1 == -1) {
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmSpan.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmSpan.h	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmSpan.h	(revision 29060)
@@ -10,6 +10,4 @@
 # ifndef PM_SPAN_H
 # define PM_SPAN_H
-
-#include <pslib.h>
 
 /// @addtogroup Objects Object Detection / Analysis Functions
Index: branches/sc_branches/trunkTest/psModules/src/objects/pmTrend2D.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/objects/pmTrend2D.h	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/objects/pmTrend2D.h	(revision 29060)
@@ -12,6 +12,4 @@
 # ifndef PM_TREND_2D_H
 # define PM_TREND_2D_H
-
-#include <pslib.h>
 
 /// @addtogroup Objects Object Detection / Analysis Functions
Index: branches/sc_branches/trunkTest/psModules/src/psmodules.h
===================================================================
--- branches/sc_branches/trunkTest/psModules/src/psmodules.h	(revision 28957)
+++ branches/sc_branches/trunkTest/psModules/src/psmodules.h	(revision 29060)
@@ -10,4 +10,5 @@
 #include <pmKapaPlots.h>
 #include <pmVisual.h>
+#include <pmVisualUtils.h>
 #include <ippStages.h>
 #include <ippDiffMode.h>
@@ -113,4 +114,8 @@
 
 // the following headers are from psModule:objects
+#include <pmTrend2D.h>
+#include <pmResiduals.h>
+#include <pmGrowthCurve.h>
+
 #include <pmSpan.h>
 #include <pmFootprintSpans.h>
@@ -119,23 +124,23 @@
 #include <pmDetections.h>
 #include <pmMoments.h>
+
+#include <pmModelFuncs.h>
+#include <pmModel.h>
+
+#include <pmSourceMasks.h>
 #include <pmSourceExtendedPars.h>
 #include <pmSourceDiffStats.h>
-#include <pmResiduals.h>
-#include <pmGrowthCurve.h>
-#include <pmTrend2D.h>
+#include <pmSource.h>
+#include <pmSourceFitModel.h>
 #include <pmPSF.h>
-#include <pmModel.h>
-#include <pmSourceMasks.h>
-#include <pmSource.h>
+#include <pmPSFtry.h>
 #include <pmPhotObj.h>
 #include <pmSourceUtils.h>
 #include <pmSourceIO.h>
 #include <pmSourceSky.h>
-#include <pmSourceFitModel.h>
 #include <pmSourceFitSet.h>
 #include <pmSourceContour.h>
 #include <pmSourcePlots.h>
 #include <pmPSF_IO.h>
-#include <pmPSFtry.h>
 #include <pmModelClass.h>
 #include <pmModelUtils.h>
@@ -144,4 +149,5 @@
 #include <pmSourceMatch.h>
 #include <pmDetEff.h>
+#include <pmPCMdata.h>
 
 // The following headers are from random locations, here because they cross bounds
