Index: branches/eam_branches/ipp-ops-20130712/psModules/src/astrom/Makefile.am
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/astrom/Makefile.am	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/astrom/Makefile.am	(revision 37066)
@@ -11,5 +11,6 @@
 	pmAstrometryRefstars.c \
 	pmAstrometryWCS.c \
-	pmAstrometryVisual.c 
+	pmAstrometryVisual.c \
+	pmKHcorrect.c
 
 pkginclude_HEADERS = \
@@ -21,5 +22,6 @@
 	pmAstrometryRefstars.h \
 	pmAstrometryWCS.h \
-	pmAstrometryVisual.h
+	pmAstrometryVisual.h \
+	pmKHcorrect.h
 
 CLEANFILES = *~
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/astrom/pmAstrometryModel.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/astrom/pmAstrometryModel.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/astrom/pmAstrometryModel.c	(revision 37066)
@@ -39,4 +39,5 @@
 #include "pmFPAExtent.h"
 #include "pmFPAfileFitsIO.h"
+#include "pmConcepts.h"
 #include "pmAstrometryWCS.h"
 #include "pmAstrometryUtils.h"
@@ -452,28 +453,4 @@
 }
 
-int pmConceptsChipNumberFromName (pmFPA *fpa, char *name) {
-
-    for (int i = 0; i < fpa->chips->n; i++) {
-        pmChip *chip = fpa->chips->data[i];
-        if (!chip) continue;
-        char *thisone = psMetadataLookupStr (NULL, chip->concepts, "CHIP.NAME");
-        if (!thisone) continue;
-        if (!strcmp (name, thisone)) return (i);
-    }
-    return -1;
-}
-
-pmChip *pmConceptsChipFromName (pmFPA *fpa, char *name) {
-
-    for (int i = 0; i < fpa->chips->n; i++) {
-        pmChip *chip = fpa->chips->data[i];
-        if (!chip) continue;
-        char *thisone = psMetadataLookupStr (NULL, chip->concepts, "CHIP.NAME");
-        if (!thisone) continue;
-        if (!strcmp (name, thisone)) return (chip);
-    }
-    return NULL;
-}
-
 // first layer converts Chip to Focal Plane
 bool pmAstromModelReadChips (pmFPAfile *file) {
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/astrom/pmAstrometryModel.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/astrom/pmAstrometryModel.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/astrom/pmAstrometryModel.h	(revision 37066)
@@ -27,7 +27,4 @@
 bool pmAstromModelWriteChips (pmFPAfile *file);
 
-int pmConceptsChipNumberFromName (pmFPA *fpa, char *name);
-pmChip *pmConceptsChipFromName (pmFPA *fpa, char *name);
-
 bool pmAstromModelReadForView (const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
 bool pmAstromModelReadFPA (pmFPAfile *file);
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/astrom/pmAstrometryObjects.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/astrom/pmAstrometryObjects.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/astrom/pmAstrometryObjects.c	(revision 37066)
@@ -571,13 +571,21 @@
     psMemSetDeallocator (stats, (psFreeFunc)pmAstromStatsFree);
 
-    //    stats->center = {0, 0, 0, 0};
-    //    stats->offset = {0, 0, 0, 0};
-    stats->angle     = 0.0;
-    stats->scale     = 1.0;
-    stats->minMetric = 0.0;
-    stats->minVar    = 0.0;
-    stats->nMatch    = 0;
-    stats->nTest     = 0;
-    stats->nSigma    = 0;
+    stats->center.x    = 0;
+    stats->center.y    = 0;
+    stats->center.xErr = 0;
+    stats->center.yErr = 0;
+
+    stats->offset.x    = 0;
+    stats->offset.y    = 0;
+    stats->offset.xErr = 0;
+    stats->offset.yErr = 0;
+
+    stats->angle       = 0.0;
+    stats->scale       = 1.0;
+    stats->minMetric   = 0.0;
+    stats->minVar      = 0.0;
+    stats->nMatch      = 0;
+    stats->nTest       = 0;
+    stats->nSigma      = 0;
 
     return (stats);
@@ -914,6 +922,5 @@
     // fprintf (stderr, "sigma: nMatch: %d, nTest: %d, nTen: %d\n", stats->nMatch, stats->nTest, sort->data.U32[sort->n - 10]);
 
-
-  psFree (sort);
+    psFree (sort);
     psFree (listNP);
     psFree (gridNP);
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/astrom/pmAstrometryObjects.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/astrom/pmAstrometryObjects.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/astrom/pmAstrometryObjects.h	(revision 37066)
@@ -42,4 +42,5 @@
     float Color;			///< object color 
     float dMag;				///< error on object magnitude
+    float SBinst;			///< surface brightness, used for Koppenhoefer correction
 }
 pmAstromObj;
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/astrom/pmAstrometryVisual.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/astrom/pmAstrometryVisual.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/astrom/pmAstrometryVisual.c	(revision 37066)
@@ -946,5 +946,5 @@
     KapaSendLabel (kapa2, "X (FP)", KAPA_LABEL_XM);
     KapaSendLabel (kapa2, "Y (FP)", KAPA_LABEL_YM);
-    KapaSendLabel (kapa2, "pmAstromGridAngle residuals. Box: Correlation Peak.", KAPA_LABEL_XP);
+    KapaSendLabel (kapa2, "pmAstromGridAngle red: raw, black: ref.", KAPA_LABEL_XP);
 
     // plot the REF data.  (also calculate the plot ranges, accumulate the plot vectors)
@@ -966,13 +966,18 @@
     KapaSetLimits(kapa2, &graphdata);
 
-    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN);
+    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN | PS_STAT_MIN  | PS_STAT_MAX );
     psVectorStats (stats, zPlot, NULL, NULL, 0);
-    float zero = stats->sampleMedian + 3.0;
-    float range = 6.0;
-
+    float range = stats->max - stats->min;
+    range = PS_MAX (0.5, PS_MIN (6.0, range));
+    float zero = stats->sampleMedian + 0.25*range;
+
+    float maxZ = zPlot->data.F32[0], minZ = zPlot->data.F32[0];
     for (int i = 0; i < zPlot->n; i++) {
+	maxZ = PS_MAX (maxZ, zPlot->data.F32[i]);
+	minZ = PS_MIN (minZ, zPlot->data.F32[i]);
 	float value = (zero - zPlot->data.F32[i]) / range;
 	zPlot->data.F32[i] = PS_MAX(0.0, PS_MIN(1.0, value));
     }
+    fprintf (stderr, "ref mags: %f to %f (%f median)\n", minZ, maxZ, stats->sampleMedian);
 
     // the point size will be scaled from the z vector
@@ -998,11 +1003,18 @@
     psStatsInit(stats);
     psVectorStats (stats, zPlot, NULL, NULL, 0);
-    zero = stats->sampleMedian + 3.0;
-    range = 6.0;
-
+    range = stats->max - stats->min;
+    range = PS_MAX (0.5, PS_MIN (6.0, range));
+    zero = stats->sampleMedian + 0.25*range;
+    // zero = stats->sampleMedian + 1.0;
+    // range = 6.0;
+
+    maxZ = zPlot->data.F32[0], minZ = zPlot->data.F32[0];
     for (int i = 0; i < zPlot->n; i++) {
+	maxZ = PS_MAX (maxZ, zPlot->data.F32[i]);
+	minZ = PS_MIN (minZ, zPlot->data.F32[i]);
 	float value = (zero - zPlot->data.F32[i]) / range;
 	zPlot->data.F32[i] = PS_MAX(0.0, PS_MIN(1.0, value));
     }
+    fprintf (stderr, "raw mags: %f to %f (%f median)\n", minZ, maxZ, stats->sampleMedian);
 
     // the point size will be scaled from the z vector
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/astrom/pmAstrometryWCS.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/astrom/pmAstrometryWCS.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/astrom/pmAstrometryWCS.c	(revision 37066)
@@ -576,4 +576,11 @@
     psFree (region);
 
+    // XXX if the inversion fails, we probably do not have a valid transform anyway
+    if (!chip->fromFPA) {
+      psWarning ("failed to find a valid transformation");
+      psFree (chip->toFPA);
+      return false;
+    }
+
     // this can take a very long time...
     while (fpa->toSky->R < 0)
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/astrom/pmKHcorrect.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/astrom/pmKHcorrect.c	(revision 37066)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/astrom/pmKHcorrect.c	(revision 37066)
@@ -0,0 +1,252 @@
+/** @file  pmKHcorrect.c
+ *  @brief Functions to read (and write?) Koppenhoefer correction file
+ *
+ *  The Koppenhoefer correction is needed for some chips of gpc1 before the camera voltages were adjusted 2011/05/11.
+ *  The correction is a modification of the X (and possibly Y) coordinate of a star which depends on the instrumental 
+ *  surface brightness, defined as -2.5 log_10 (DN) + 5.0 log_10 fwhm_maj [XXX be careful about the definition of fwhm_maj]
+ *
+ *  @ingroup AstroImage
+ *  @author EAM, IfA
+ *
+ *  Copyright 2014 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/******************************************************************************/
+/*  INCLUDE FILES                                                             */
+/******************************************************************************/
+#include <stdio.h>
+#include <strings.h>
+#include <string.h>
+#include <math.h>
+#include <assert.h>
+#include <unistd.h>   // for unlink
+#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 "pmFPAExtent.h"
+#include "pmFPAfileFitsIO.h"
+#include "pmConcepts.h"
+#include "pmKHcorrect.h"
+
+static void KHcorrectDataFree (KHcorrectData *spline) {
+
+    if (!spline) return;
+
+    psFree (spline->xk);
+    psFree (spline->yk);
+    psFree (spline->y2);
+    return;
+}
+
+KHcorrectData *KHcorrectDataAlloc (int Nrow) {
+
+    // allocate xk[Nrow], etc
+
+    KHcorrectData *spline = (KHcorrectData *) psAlloc(sizeof(KHcorrectData));
+    psMemSetDeallocator(spline, (psFreeFunc) KHcorrectDataFree);
+
+    spline->N  = Nrow;
+    spline->xk = (float *) psAlloc(Nrow*sizeof(float));
+    spline->yk = (float *) psAlloc(Nrow*sizeof(float));
+    spline->y2 = (float *) psAlloc(Nrow*sizeof(float));
+
+    return spline;
+}
+
+// the KH correction is a function of the instrumental surface brightness, SBinst
+float KHcorrectApply (KHcorrectData *spline, float X) {
+
+    int N = spline->N;
+
+    // saturate correction at high and low ends
+    if (X < spline->xk[  0]) return spline->yk[  0];
+    if (X > spline->xk[N-1]) return spline->yk[N-1];
+
+    float *xk = spline->xk;
+    float *yk = spline->yk;
+    float *y2 = spline->y2;
+
+    /* find correct element in array (x must be sorted) */
+    int lo = 0;
+    int hi = N-1;
+    while (hi - lo > 1) {
+	int i = 0.5*(hi+lo);
+	if (xk[i] > X) {
+	    hi = i;
+	} else {
+	    lo = i;
+	}
+    }
+
+    /* error condition: duplicate abssisca */
+    float dx = xk[hi] - xk[lo];
+    if (dx == 0.0) {
+	return (0.0);
+    }
+
+    /* evaluate spline */
+    float a = (xk[hi] - X) / dx;
+    float b = (X - xk[lo]) / dx;
+
+    float value = a*yk[lo] + b*yk[hi] + ((a*a - 1.0)*a*y2[lo] + (b*b - 1.0)*b*y2[hi])*(dx*dx) / 6.0;
+    return (value);
+}
+
+/********************* CheckDataStatus functions *****************************/
+
+bool pmKHcorrectCheckDataStatusForView (const pmFPAview *view, pmFPAfile *file) {
+    psError(PS_ERR_IO, false, "Check Data Status not defined");
+    return false;
+}
+
+bool pmKHcorrectCheckDataStatusForFPA (const pmFPA *fpa) {
+    psError(PS_ERR_IO, false, "Check Data Status not defined");
+    return false;
+}
+
+bool pmKHcorrectCheckDataStatusForChip (const pmChip *chip) {
+    psError(PS_ERR_IO, false, "Check Data Status not defined");
+    return false;
+}
+
+/********************* Write Data functions *****************************/
+
+// NOTE : these are not exposed because I don't think we need them (we do not create KHcorrect
+// in psModules based tool, but in DVO.
+
+bool pmKHcorrectWriteFPA (pmFPAfile *file, const pmFPA *fpa);
+bool pmKHcorrectWriteForView (const pmFPAview *view, pmFPAfile *file, pmConfig *config)
+{
+    // write the full model in one pass: require the level to be FPA
+    if (view->chip != -1) {
+        psError(PS_ERR_IO, false, "Koppenhoefer Correction must be written at the FPA level");
+        return false;
+    }
+
+    pmFPA *fpa = pmFPAfileSuitableFPA(file, view, config, false); // Suitable FPA for writing
+
+    if (!pmKHcorrectWriteFPA(file, fpa)) {
+        psError(PS_ERR_IO, false, "Failed to write KH Correction for fpa");
+        psFree(fpa);
+        return false;
+    }
+
+    psFree(fpa);
+
+    return true;
+}
+
+// write out all chip-level KH Correction data for this FPA
+bool pmKHcorrectWriteFPA (pmFPAfile *file, const pmFPA *fpa)
+{
+    psError(PS_ERR_IO, false, "output for KH Correction is not defined");
+    return false;
+}
+
+/********************* Read Data functions *****************************/
+
+bool pmKHcorrectReadForView (const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
+    {
+        // read the full model in one pass: require the level to be FPA
+        if (view->chip != -1) {
+            psError(PS_ERR_IO, false, "KH Correction must be read at the FPA level");
+            return false;
+        }
+
+        if (!pmKHcorrectReadFPA (file)) {
+            psError(PS_ERR_IO, false, "Failed to read KH Correction for fpa");
+            return false;
+        }
+        return true;
+    }
+
+// read in all chip-level KH Correction data for this FPA
+bool pmKHcorrectReadFPA (pmFPAfile *file) {
+
+    if (!pmKHcorrectReadChips (file)) {
+        psError(PS_ERR_IO, false, "Failed to read KH Correction for chips");
+        return false;
+    }
+
+    return true;
+}
+
+// read the set of tables, one for each chip
+bool pmKHcorrectReadChips (pmFPAfile *file) {
+
+    bool haveData, status;
+
+    // loop over the extensions
+    // for each extension, use the extname (eg, XY01.DX.T0) to assign to a chip
+
+    // move to the start of the file
+    haveData = psFitsMoveExtNum (file->fits, 1, false);
+    if (!haveData) {
+        psError(PS_ERR_IO, false, "Failed to read even the first extension?");
+        return false;
+    }
+
+    while (haveData) {
+
+	// load the header
+	psMetadata *header = psFitsReadHeader(NULL, file->fits); // The FITS header
+	if (!header) psAbort("cannot read model header");
+
+	// load the full model in one shot
+	psArray *model = psFitsReadTable (file->fits);
+	if (!model) psAbort("cannot read model");
+	
+	// determine the chip:
+	char *extname = psMetadataLookupStr (&status, header, "EXTNAME");
+	psLogMsg ("psModules.astrom", 4, "read %ld rows from Koppenhoefer correction file, extname %s\n", model->n, extname);
+
+	// I expect to find a name of the form: chipName.dir.tset (eg, XY01.DX.T0)
+	// where chipName like 'XY01'
+	// dir = 'DX' 
+	// tset = 'T0'
+	psAssert (strlen(extname) == 10, "invalid extension %s", extname);
+	psAssert (extname[5] == 'D', "invalid extension %s", extname);
+	psAssert (extname[6] == 'X', "invalid extension %s", extname);
+	psAssert (extname[8] == 'T', "invalid extension %s", extname);
+	psAssert (extname[9] == '0', "invalid extension %s", extname);
+
+	char chipName[5];
+	strncpy (chipName, extname, 4);
+	chipName[4] = 0;
+
+	pmChip *chip = pmConceptsChipFromName (file->fpa, chipName);
+	if (!chip) psAbort ("invalid chip?");
+
+	KHcorrectData *spline = KHcorrectDataAlloc (model->n);
+
+	// parse the model entries
+	for (int i = 0; i < model->n; i++) {
+	    psMetadata *row = model->data[i];
+
+	    spline->xk[i] = psMetadataLookupF32(&status, row, "X_KNOT");
+	    spline->yk[i] = psMetadataLookupF32(&status, row, "Y_KNOT");
+	    spline->y2[i] = psMetadataLookupF32(&status, row, "DY2_DX");
+	}
+	psMetadataAddUnknown (chip->analysis, PS_LIST_TAIL, "KH.CORRECT", PS_META_REPLACE, "", spline);
+	psFree (spline);
+
+	psFree (model);
+	psFree (header);
+
+	// move to the next extension
+	haveData = psFitsMoveExtNum (file->fits, 1, true);
+    }
+
+    return true;
+}
+
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/astrom/pmKHcorrect.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/astrom/pmKHcorrect.h	(revision 37066)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/astrom/pmKHcorrect.h	(revision 37066)
@@ -0,0 +1,35 @@
+/** @file  pmKHcorrect.h
+ *  @brief Functions to read (and write?) Koppenhoefer correction file
+ *
+ *  The Koppenhoefer correction is needed for some chips of gpc1 before the camera voltages were adjusted 2011/05/11.
+ *  The correction is a modification of the X (and possibly Y) coordinate of a star which depends on the instrumental 
+ *  surface brightness, defined as -2.5 log_10 (DN) + 5.0 log_10 fwhm_maj [XXX be careful about the definition of fwhm_maj]
+ *
+ *  @ingroup AstroImage
+ *  @author EAM, IfA
+ *
+ *  Copyright 2014 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_KH_CORRECT_H
+#define PM_KH_CORRECT_H
+
+/// @addtogroup Astrometry
+/// @{
+
+typedef struct {
+    int N;
+    float *xk;
+    float *yk;
+    float *y2;
+} KHcorrectData;
+
+KHcorrectData *KHcorrectDataAlloc (int Nrow);
+float KHcorrectApply (KHcorrectData *spline, float X);
+
+bool pmKHcorrectReadForView (const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
+bool pmKHcorrectReadFPA (pmFPAfile *file);
+bool pmKHcorrectReadChips (pmFPAfile *file);
+
+/// @}
+#endif
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/camera/pmFPAfile.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/camera/pmFPAfile.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/camera/pmFPAfile.c	(revision 37066)
@@ -492,4 +492,7 @@
         return PM_FPA_FILE_CMF;
     }
+    if (!strcasecmp(type, "CFF"))     {
+        return PM_FPA_FILE_CFF;
+    }
     if (!strcasecmp(type, "WCS"))     {
         return PM_FPA_FILE_WCS;
@@ -528,7 +531,6 @@
       return PM_FPA_FILE_LINEARITY;
     }
-    // deprecate this?
     if (!strcasecmp(type, "ASTROM"))     {
-        return PM_FPA_FILE_ASTROM_MODEL;
+      return PM_FPA_FILE_ASTROM_MODEL;
     }
     if (!strcasecmp(type, "ASTROM.MODEL"))     {
@@ -537,4 +539,7 @@
     if (!strcasecmp(type, "ASTROM.REFSTARS"))     {
         return PM_FPA_FILE_ASTROM_REFSTARS;
+    }
+    if (!strcasecmp(type, "KH.CORRECT"))     {
+        return PM_FPA_FILE_KH_CORRECT;
     }
     if (!strcasecmp(type, "SUBKERNEL"))     {
@@ -562,4 +567,6 @@
       case PM_FPA_FILE_CMF:
         return ("CMF");
+      case PM_FPA_FILE_CFF:
+        return ("CFF");
       case PM_FPA_FILE_WCS:
         return ("WCS");
@@ -588,4 +595,6 @@
       case PM_FPA_FILE_ASTROM_REFSTARS:
         return ("ASTROM.REFSTARS");
+      case PM_FPA_FILE_KH_CORRECT:
+        return ("KH.CORRECT");
       case PM_FPA_FILE_SUBKERNEL:
         return ("SUBKERNEL");
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/camera/pmFPAfile.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/camera/pmFPAfile.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/camera/pmFPAfile.h	(revision 37066)
@@ -34,4 +34,5 @@
     PM_FPA_FILE_CMP,
     PM_FPA_FILE_CMF,
+    PM_FPA_FILE_CFF,
     PM_FPA_FILE_WCS,
     PM_FPA_FILE_RAW,
@@ -47,4 +48,5 @@
     PM_FPA_FILE_ASTROM_MODEL,
     PM_FPA_FILE_ASTROM_REFSTARS,
+    PM_FPA_FILE_KH_CORRECT,
     PM_FPA_FILE_SUBKERNEL,
     PM_FPA_FILE_SRCTEXT,
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/camera/pmFPAfileDefine.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/camera/pmFPAfileDefine.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/camera/pmFPAfileDefine.c	(revision 37066)
@@ -103,7 +103,13 @@
 
     type = psMetadataLookupStr(&status, data, "FILE.TYPE");
+    if (!type) {
+        psError(PM_ERR_CONFIG, true, "FILE.TYPE is not defined for %s\n", name);
+        psFree(file);
+        return NULL;
+    }
+
     file->type = pmFPAfileTypeFromString(type);
     if (file->type == PM_FPA_FILE_NONE) {
-        psError(PM_ERR_CONFIG, true, "FILE.TYPE is not defined for %s\n", name);
+        psError(PM_ERR_CONFIG, true, "FILE.TYPE %s is not registered in pmFPAfile.c:pmFPAfileTypeFromString\n", type);
         psFree(file);
         return NULL;
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/camera/pmFPAfileFitsIO.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/camera/pmFPAfileFitsIO.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/camera/pmFPAfileFitsIO.c	(revision 37066)
@@ -150,5 +150,7 @@
       case PM_FPA_FILE_PSF:
       case PM_FPA_FILE_ASTROM_MODEL:
-      case PM_FPA_FILE_ASTROM_REFSTARS: {
+      case PM_FPA_FILE_ASTROM_REFSTARS: 
+      case PM_FPA_FILE_KH_CORRECT:
+	{
           pmHDU *hdu = pmFPAviewThisHDU(view, fpa);
           if (hdu) {
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/camera/pmFPAfileIO.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/camera/pmFPAfileIO.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/camera/pmFPAfileIO.c	(revision 37066)
@@ -34,4 +34,5 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmSourceMasks.h"
@@ -39,4 +40,5 @@
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -47,4 +49,5 @@
 #include "pmPSF_IO.h"
 
+#include "pmKHcorrect.h"
 #include "pmAstrometryModel.h"
 #include "pmAstrometryRefstars.h"
@@ -222,4 +225,5 @@
       case PM_FPA_FILE_CMP:
       case PM_FPA_FILE_CMF:
+      case PM_FPA_FILE_CFF:
       case PM_FPA_FILE_WCS:
       case PM_FPA_FILE_SRCTEXT:
@@ -231,4 +235,7 @@
       case PM_FPA_FILE_ASTROM_MODEL:
         status = pmAstromModelReadForView (view, file, config);
+        break;
+      case PM_FPA_FILE_KH_CORRECT:
+        status = pmKHcorrectReadForView (view, file, config);
         break;
       case PM_FPA_FILE_EXPNUM:
@@ -317,8 +324,10 @@
       case PM_FPA_FILE_CMP:
       case PM_FPA_FILE_CMF:
+      case PM_FPA_FILE_CFF:
       case PM_FPA_FILE_WCS:
       case PM_FPA_FILE_PSF:
       case PM_FPA_FILE_ASTROM_MODEL:
       case PM_FPA_FILE_ASTROM_REFSTARS:
+      case PM_FPA_FILE_KH_CORRECT:
       case PM_FPA_FILE_JPEG:
       case PM_FPA_FILE_KAPA:
@@ -405,4 +414,8 @@
       }
     }
+    if (file->type == PM_FPA_FILE_KH_CORRECT) {
+      psTrace("psModules.camera", 6, "skip write for %s, no write function defined", file->name);
+      return true;
+    }
 
     // open the file if not yet opened
@@ -483,4 +496,5 @@
       case PM_FPA_FILE_CMP:
       case PM_FPA_FILE_CMF:
+      case PM_FPA_FILE_CFF:
         status = pmFPAviewWriteObjects (view, file, config);
         break;
@@ -496,4 +510,8 @@
       case PM_FPA_FILE_ASTROM_REFSTARS:
         status = pmAstromRefstarsWriteForView (view, file, config);
+        break;
+
+      case PM_FPA_FILE_KH_CORRECT:
+        psError(PS_ERR_IO, true, "cannot write type KH.CORRECT (%s)", file->name);
         break;
 
@@ -567,8 +585,10 @@
       case PM_FPA_FILE_PATTERN:
       case PM_FPA_FILE_CMF:
+      case PM_FPA_FILE_CFF:
       case PM_FPA_FILE_WCS:
       case PM_FPA_FILE_PSF:
       case PM_FPA_FILE_ASTROM_MODEL:
       case PM_FPA_FILE_ASTROM_REFSTARS:
+      case PM_FPA_FILE_KH_CORRECT:
       case PM_FPA_FILE_LINEARITY:
       case PM_FPA_FILE_EXPNUM:
@@ -643,8 +663,10 @@
       case PM_FPA_FILE_CMP:
       case PM_FPA_FILE_CMF:
+      case PM_FPA_FILE_CFF:
       case PM_FPA_FILE_WCS:
       case PM_FPA_FILE_PSF:
       case PM_FPA_FILE_ASTROM_MODEL:
       case PM_FPA_FILE_ASTROM_REFSTARS:
+      case PM_FPA_FILE_KH_CORRECT:
       case PM_FPA_FILE_EXPNUM:
         psTrace ("psModules.camera", 6, "NOT freeing %s (%s) : save for further analysis\n", file->filename, file->name);
@@ -804,8 +826,10 @@
       case PM_FPA_FILE_PATTERN:
       case PM_FPA_FILE_CMF:
+      case PM_FPA_FILE_CFF:
       case PM_FPA_FILE_WCS:
       case PM_FPA_FILE_PSF:
       case PM_FPA_FILE_ASTROM_MODEL:
       case PM_FPA_FILE_ASTROM_REFSTARS:
+      case PM_FPA_FILE_KH_CORRECT:
       case PM_FPA_FILE_LINEARITY:
       case PM_FPA_FILE_EXPNUM:
@@ -1011,4 +1035,5 @@
       case PM_FPA_FILE_EXPNUM:
       case PM_FPA_FILE_ASTROM_MODEL:
+      case PM_FPA_FILE_KH_CORRECT:
       case PM_FPA_FILE_SX:
       case PM_FPA_FILE_RAW:
@@ -1016,4 +1041,5 @@
       case PM_FPA_FILE_CMP:
       case PM_FPA_FILE_WCS:
+      case PM_FPA_FILE_CFF:
       case PM_FPA_FILE_JPEG:
       case PM_FPA_FILE_KAPA:
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/camera/pmReadoutFake.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/camera/pmReadoutFake.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/camera/pmReadoutFake.c	(revision 37066)
@@ -20,11 +20,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -54,5 +55,5 @@
 
     psF32 *params = model->params->data.F32; // Model parameters
-    psEllipseAxes axes = pmPSF_ModelToAxes(params, model->type); // Ellipse axes
+    psEllipseAxes axes = pmPSF_ModelToAxes(params, model->class->useReff); // Ellipse axes
     // Curiously, the minor axis can be larger than the major axis, so need to check.
     if (axes.major >= axes.minor) {
@@ -61,5 +62,5 @@
         axes.major = axes.minor;
     }
-    return pmPSF_AxesToModel(params, axes, model->type);
+    return pmPSF_AxesToModel(params, axes, model->class->useReff);
 }
 
@@ -121,5 +122,5 @@
             }
 
-            flux /= normModel->modelFlux(normModel->params);
+            flux /= normModel->class->modelFlux(normModel->params);
             psFree(normModel);
         }
@@ -163,5 +164,5 @@
         float fakeRadius = 1.0;         // Radius of fake source
         if (isfinite(minFlux)) {
-            fakeRadius = PS_MAX(fakeRadius, fakeModel->modelRadius(fakeModel->params, minFlux));
+            fakeRadius = PS_MAX(fakeRadius, fakeModel->class->modelRadius(fakeModel->params, minFlux));
         }
         if (radius > 0) {
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/concepts/pmConcepts.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/concepts/pmConcepts.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/concepts/pmConcepts.c	(revision 37066)
@@ -578,2 +578,26 @@
 }
 
+int pmConceptsChipNumberFromName (pmFPA *fpa, char *name) {
+
+    for (int i = 0; i < fpa->chips->n; i++) {
+        pmChip *chip = fpa->chips->data[i];
+        if (!chip) continue;
+        char *thisone = psMetadataLookupStr (NULL, chip->concepts, "CHIP.NAME");
+        if (!thisone) continue;
+        if (!strcmp (name, thisone)) return (i);
+    }
+    return -1;
+}
+
+pmChip *pmConceptsChipFromName (pmFPA *fpa, char *name) {
+
+    for (int i = 0; i < fpa->chips->n; i++) {
+        pmChip *chip = fpa->chips->data[i];
+        if (!chip) continue;
+        char *thisone = psMetadataLookupStr (NULL, chip->concepts, "CHIP.NAME");
+        if (!thisone) continue;
+        if (!strcmp (name, thisone)) return (chip);
+    }
+    return NULL;
+}
+
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/concepts/pmConcepts.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/concepts/pmConcepts.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/concepts/pmConcepts.h	(revision 37066)
@@ -161,4 +161,8 @@
     );
 
+// some utility functions:
+int pmConceptsChipNumberFromName (pmFPA *fpa, char *name);
+pmChip *pmConceptsChipFromName (pmFPA *fpa, char *name);
+
 /// @}
 #endif
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/config/pmConfig.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/config/pmConfig.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/config/pmConfig.c	(revision 37066)
@@ -965,4 +965,42 @@
                                   "Original replaced by -R option", newRule);
         }
+        psFree(camerasIter);
+    }
+
+    // Look for command-line options for files to replace
+    while ((argNum = psArgumentGet(*argc, argv, "-photcode-rule")) > 0) {
+        psArgumentRemove(argNum, argc, argv);
+        if (argNum >= *argc) {
+            psError(PM_ERR_CONFIG, true,
+                    "-photcode-rule provided without new rule.");
+            psFree(config);
+            return NULL;
+        }
+
+        psString newrule = psStringCopy(argv[argNum]); // The filerule, to be modified
+        psArgumentRemove(argNum, argc, argv);
+
+        psMetadata *cameras = psMetadataLookupMetadata(NULL, config->system, "CAMERAS"); // List of cameras
+        if (!cameras) {
+            psError(PM_ERR_CONFIG, false, "Unable to find CAMERAS in the site configuration.\n");
+            return false;
+        }
+
+        psMetadataIterator *camerasIter = psMetadataIteratorAlloc(cameras, PS_LIST_HEAD, NULL); // Iterator
+        psMetadataItem *cameraItem;     // Item from iteration
+        while ((cameraItem = psMetadataGetAndIncrement(camerasIter))) {
+            // Silently ignore problems --- they will be caught later, because if the user wants the nominated
+            // file and it's not available for that camera, then they will know.
+
+            if (cameraItem->type != PS_DATA_METADATA) {
+                psTrace("psModules.config", 2,
+                        "Entry %s in CAMERAS is not of type METADATA --- ignored.", cameraItem->name);
+                continue;
+            }
+            psMetadata *camera = cameraItem->data.md; // Camera configuration
+
+	    psMetadataAddStr (camera, PS_LIST_TAIL, "PHOTCODE.RULE", PS_META_REPLACE, "original replaced by -photcode-rule option", newrule);
+        }
+	psFree(newrule);
         psFree(camerasIter);
     }
@@ -1860,5 +1898,5 @@
     }
 
-    return psMetadataLookupMetadata(NULL, filerules, realname);
+    return psMetadataLookupMetadata(&mdok, filerules, realname);
 }
 
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/detrend/pmDetrendDB.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/detrend/pmDetrendDB.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/detrend/pmDetrendDB.c	(revision 37066)
@@ -111,4 +111,5 @@
 	DETREND_STRING_CASE(LINEARITY);
 	DETREND_STRING_CASE(AUXMASK);
+	DETREND_STRING_CASE(KH_CORRECT);
     default:
         return NULL;
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/detrend/pmDetrendDB.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/detrend/pmDetrendDB.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/detrend/pmDetrendDB.h	(revision 37066)
@@ -40,4 +40,5 @@
     PM_DETREND_TYPE_LINEARITY,
     PM_DETREND_TYPE_AUXMASK,
+    PM_DETREND_TYPE_KH_CORRECT,
 } pmDetrendType;
 
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/detrend/pmPattern.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/detrend/pmPattern.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/detrend/pmPattern.c	(revision 37066)
@@ -1222,6 +1222,8 @@
     }
     double max_XX = 0;
+#if (PS_TRACE_ON)
     double solution_V = 0;
     int i_peak = -1;
+#endif
     for (int i = 0; i < numChips + 4; i++) { // If any cells have no value of themself, set the matrix to 1.0.
       if (XX->data.F64[i][i] == 0.0) {
@@ -1230,6 +1232,8 @@
       if (XX->data.F64[i][i] > max_XX) {
 	max_XX = XX->data.F64[i][i];
+#if (PS_TRACE_ON)
 	solution_V = solution->data.F64[i];
 	i_peak = i;
+#endif
       }
     }
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/extras/pmThreadTools.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/extras/pmThreadTools.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/extras/pmThreadTools.c	(revision 37066)
@@ -29,7 +29,7 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
@@ -37,4 +37,5 @@
 #include "pmSourcePhotometry.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/extras/pmVisual.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/extras/pmVisual.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/extras/pmVisual.c	(revision 37066)
@@ -22,32 +22,5 @@
 bool pmSourceVisualClose(void);
 
-// #include "pmHDU.h"
-// #include "pmFPA.h"
-// #include "pmFPAfile.h"
-// #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 "pmSourceSatstar.h"
-// #include "pmSource.h"
-// #include "pmSourceFitModel.h"
-// #include "pmPSF.h"
-// #include "pmPSFtry.h"
-// #include "pmFPAExtent.h"
-// #include "pmAstrometryVisual.h"
-// #include "pmSubtractionVisual.h"
-// #include "pmStackVisual.h"
-// #include "pmSourceVisual.h"
 
 # if (HAVE_KAPA)
@@ -160,4 +133,77 @@
 }
 
+
+// ask the user to continue or not.  give up after 2 seconds.
+bool pmVisualAskUserOrDump(bool *plotFlag, bool *dumpData)
+{
+    struct timeval timeout;
+    fd_set fdSet;
+    int status;
+
+    if (dumpData) *dumpData = false;
+
+    char key[10];
+    if (plotFlag && dumpData) {
+	fprintf (stderr, "[p]ause? [c]ontinue? [s]kip the rest of these plots? [d]ump the data? [a]bort all visual plots? (c) ");
+    } 
+    if (plotFlag && !dumpData) {
+	fprintf (stderr, "[p]ause? [c]ontinue? [s]kip the rest of these plots? [a]bort all visual plots? (c) ");
+    } 
+    if (!plotFlag && dumpData) {
+	fprintf (stderr, "[p]ause? [c]ontinue? [d]ump the data? [a]bort all visual plots? (c) ");
+    } 
+    if (!plotFlag && !dumpData) {
+	fprintf (stderr, "[p]ause? [c]ontinue? [a]bort all visual plots? (c) ");
+    }
+
+    /* Wait up to 1.0 second for a response, then continue */
+    timeout.tv_sec = 10;
+    timeout.tv_usec = 0;
+
+    FD_ZERO (&fdSet);
+    FD_SET (STDIN_FILENO, &fdSet);
+
+    status = select (1, &fdSet, NULL, NULL, &timeout);
+    if (status <= 0) {
+	fprintf (stderr, "\n");
+	return true; // if no data, give up
+    }
+
+    while (true) {
+	if (!fgets(key, 8, stdin)) {
+	    psWarning("Unable to read option");
+	}
+	switch (key[0]) {
+	  case 's':
+	    if (plotFlag) *plotFlag = false;
+	    return true;
+	  case 'd':
+	    if (dumpData) *dumpData = true;
+	    return true;
+	  case 'a':
+	    isVisual = false;
+	    return true;
+	  case 'c':
+	  case '\n':
+	    return true;
+	  default:
+	    break;
+	}
+	
+	if (plotFlag && dumpData) {
+	  fprintf (stderr, "[p]ause? [c]ontinue? [s]kip the rest of these plots? [d]ump the data? [a]bort all visual plots? (c) ");
+	} 
+	if (plotFlag && !dumpData) {
+	  fprintf (stderr, "[p]ause? [c]ontinue? [s]kip the rest of these plots? [a]bort all visual plots? (c) ");
+	} 
+	if (!plotFlag && dumpData) {
+	  fprintf (stderr, "[p]ause? [c]ontinue? [d]ump the data? [a]bort all visual plots? (c) ");
+	} 
+	if (!plotFlag && !dumpData) {
+	  fprintf (stderr, "[p]ause? [c]ontinue? [a]bort all visual plots? (c) ");
+	}
+    }
+    return true;
+}
 
 bool pmVisualImStats(psImage *image, double *mean, double *stdev, double *min, double *max) {
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/extras/pmVisual.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/extras/pmVisual.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/extras/pmVisual.h	(revision 37066)
@@ -52,4 +52,12 @@
  */
 bool pmVisualAskUser(bool *plotFlag);
+
+
+/** Ask the user how to proceed.
+ * At the user's request, this will disable diagnostic plotting.
+ * @param plotFlag, set to false if this plot should be disabled in the future
+ * @param dumpData, set to true if user requests a data dump
+ */
+bool pmVisualAskUserOrDump(bool *plotFlag, bool *dumpData);
 
 
@@ -138,4 +146,5 @@
 bool pmVisualInitGraph (int kapa, void *section, void *graphdata);
 bool pmVisualAskUser(bool *plotFlag);
+bool pmVisualAskUserOrDump(bool *plotFlag, bool *dumpData);
 bool pmVisualScaleImage(int kapaFD, psImage *inImage,
                         const char *name, int channel, bool clip);
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/imcombine/pmPSFEnvelope.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/imcombine/pmPSFEnvelope.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/imcombine/pmPSFEnvelope.c	(revision 37066)
@@ -20,11 +20,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -167,9 +168,9 @@
                         continue;
                     }
-                    model->modelSetLimits(PM_MODEL_LIMITS_MODERATE);
+                    model->class->modelSetLimits(PM_MODEL_LIMITS_MODERATE);
                     bool limits = true; // Model within limits?
                     for (int j = 0; j < model->params->n && limits; j++) {
-                        if (!model->modelLimits(PS_MINIMIZE_PARAM_MIN, j, model->params->data.F32, NULL) ||
-                            !model->modelLimits(PS_MINIMIZE_PARAM_MAX, j, model->params->data.F32, NULL)) {
+                        if (!model->class->modelLimits(PS_MINIMIZE_PARAM_MIN, j, model->params->data.F32, NULL) ||
+                            !model->class->modelLimits(PS_MINIMIZE_PARAM_MAX, j, model->params->data.F32, NULL)) {
                             limits = false;
                         }
@@ -245,5 +246,5 @@
                 continue;
             }
-            float srcRadius = model->modelRadius(model->params, PS_SQR(VARIANCE_VAL)); // Radius for source
+            float srcRadius = model->class->modelRadius(model->params, PS_SQR(VARIANCE_VAL)); // Radius for source
             psFree(model);
             if (srcRadius == 0) {
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/imcombine/pmStack.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/imcombine/pmStack.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/imcombine/pmStack.c	(revision 37066)
@@ -43,7 +43,11 @@
 // #define TEST_X 972
 // #define TEST_Y 3213
-#define TEST_X 3289
-#define TEST_Y 4810
-#define TEST_RADIUS 0.5                 // Radius to examine
+//#define TEST_X 3289
+//#define TEST_Y 4810
+//#define TEST_RADIUS 0.5                 // Radius to examine
+//MEH -- streak-like junk md04s065i
+#define TEST_X 1129 
+#define TEST_Y 4256
+#define TEST_RADIUS 2.0                 // Radius to examine
 # endif
 
@@ -108,5 +112,5 @@
 
 // KMM functions to do bimodality rejection of pixels
-float gaussian(float x, float m, float s) {
+double gaussian(float x, float m, float s) {
   return(pow(s * sqrt(2 * M_PI),-1) * exp(-0.5 * pow( (x - m) / s, 2)));
 }
@@ -116,5 +120,6 @@
 			 float *mU, float *sU,
 			 float *pi1, float *m1, float *s1,
-			 float *pi2, float *m2, float *s2) {
+			 float *pi2, float *m2, float *s2,
+                         int xyrdebug) {
   assert(values);
   assert(values->type.type == PS_TYPE_F32);
@@ -151,4 +156,10 @@
   }
 
+  if (xyrdebug == 1) { 
+      fprintf(stderr,"KMM uni: %d %f %d (%f %f)\n", 
+              xyrdebug,logL_unimodal,discrepant_index, 
+              *mU,*sU); 
+  } 
+
   // Do EM loop
   float dL = 0;
@@ -186,5 +197,6 @@
   *pi2 = 0.5;
 
-  float g1,g2,norm;
+  //MEH -- need to be double to help avoid 0 in norm
+  double g1,g2,norm;
   float w1,w2;
 
@@ -203,4 +215,12 @@
 /* 	      *m2,*s2,*pi2); */
 /*     } */
+
+    if (xyrdebug == 1) { 
+        fprintf(stderr,"KMM EM iter: %d %f %f %f %f (%f %f %e) (%f %f %e)\n", 
+                *iter,logL_unimodal,logL_bimodal,oldL,dL, 
+                *m1,*s1,*pi1, 
+                *m2,*s2,*pi2); 
+    } 
+
     // Expectation/P-stage
     for (i = 0; i < values->n; i++) { // Calculate probabilities for each mode
@@ -208,6 +228,18 @@
       g2 = gaussian(values->data.F32[i],*m2,*s2);
       norm = (*pi1 * g1 + *pi2 * g2);
-      P1->data.F32[i] = (*pi1 * g1) / norm;
-      P2->data.F32[i] = (*pi2 * g2) / norm;
+      //MEH -- must protect denom from norm=0
+      if (norm > 0) {
+	  P1->data.F32[i] = (*pi1 * g1) / norm;
+	  P2->data.F32[i] = (*pi2 * g2) / norm;
+      } else {
+	  P1->data.F32[i] = 0.0;
+	  P2->data.F32[i] = 0.0;
+      }	 
+ 
+      if (xyrdebug == 1) {
+          fprintf(stderr,"KMM EM-P loop: %d %d %le %le %le\n",
+                         *iter,i,norm,g1,g2);
+      }
+
     }
     // Maximization/M-stage
@@ -231,4 +263,10 @@
       w1 += P1->data.F32[i];
       w2 += P2->data.F32[i];
+
+      if (xyrdebug == 1) {
+          fprintf(stderr,"KMM EM-M loop: %d %d (%f %f %f %e) (%f %f %f %e)\n",
+                         *iter,i,*m1,values->data.F32[i],w1,P1->data.F32[i],*m2,values->data.F32[i],w2,P2->data.F32[i]);
+      }
+
     }
     *m1 /= w1;
@@ -250,10 +288,18 @@
       *pi2 = 0.0;
     }
-    if (*s1 == 0) { // sigma may not be zero
-      *s1 = KMM_SMALL_NUMBER * *m1;
-    }
-    if (*s2 == 0) { // sigma may not be zero
-      *s2 = KMM_SMALL_NUMBER * *m2;
-    }
+    if (*s1 == 0) { // sigma may not be zero -- MEH -- nor <0 and need additive offset if m~0
+      *s1 = fabsf(KMM_SMALL_NUMBER * *m1) + KMM_SMALL_NUMBER;
+    }
+    if (*s2 == 0) { // sigma may not be zero 
+      *s2 = fabsf(KMM_SMALL_NUMBER * *m2) + KMM_SMALL_NUMBER;
+    }
+
+    if (xyrdebug == 1) { 
+        fprintf(stderr,"KMM EM end: %d %f %f %f %f (%f %e %e %f) (%f %e %e %f)\n", 
+                *iter,logL_unimodal,logL_bimodal,oldL,dL, 
+                *m1,*s1,*pi1,w1, 
+                *m2,*s2,*pi2,w2); 
+    } 
+
   } // End EM phase
 
@@ -267,9 +313,15 @@
     *Punimodal = 1.0;
   }
+
+  if (xyrdebug == 1) { 
+      fprintf(stderr,"KMM calc Puni: %d %f %d %f\n", 
+              xyrdebug,lambda,df,*Punimodal); 
+  } 
+
   psFree(P1);
   psFree(P2);
 }
 
-static void KMMFindPopular(const psVector *values, float *Punimodal, float *mean, float *sigma, float *pi) {
+static void KMMFindPopular(const psVector *values, float *Punimodal, float *mean, float *sigma, float *pi, int xyrdebug) {
   float KMM_MINIMUM_PVALUE = 0.05; // Should be an option.
   float mU,sU;
@@ -283,5 +335,5 @@
 	       &mU,&sU,
 	       &pi1,&m1,&s1,
-	       &pi2,&m2,&s2);
+	       &pi2,&m2,&s2,xyrdebug);
 /*   fprintf(stdout,"%g %g : %g %g %g : %g %g %g : %g %d\t", */
 /* 	  mU,sU, */
@@ -783,4 +835,5 @@
                           psImageMaskType goodMask, // Value for good pixels
                           bool safe,           // Safe combination?
+			  int nminpix,         // Minimum number of input per pixel
                           float invTotalWeight    // Inverse of total weight for all inputs
                           )
@@ -802,5 +855,11 @@
     CHECKPIX(x, y, "bad vs good : %x %x %x\n", maskValue, badMask, blankMask);
 
-    switch (num) {
+    //MEH -- hackish adding of lower limit for input per pixel 
+    int numN = num;
+    if (num < nminpix) {
+        CHECKPIX(x, y, "Nmin (%d) inputs (%d) to combine, pixel %d,%d is manually set bad\n", nminpix, numN, x, y);
+        numN = 0;
+    }
+    switch (numN) {
       case 0: {
           // Nothing to combine: it's bad
@@ -898,4 +957,6 @@
     psVector *pixelSuspects = buffer->suspects; // Is the pixel suspect?
     psVector *pixelLimits = buffer->limits; // Is the pixel suspect?
+    //MEH -- adding a debug option for TESTING xyr position but could be better..
+    int xyrdebug = 0;
 
     // KMM values;
@@ -917,5 +978,10 @@
 	// This should probably be an option
 	if (useKMM) {
-	  KMMFindPopular(pixelData,&Punimodal,&KMMmean,&KMMsigma,&KMMpi);
+#ifdef TESTING
+            if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
+                xyrdebug = 1;
+            }
+# endif
+	  KMMFindPopular(pixelData,&Punimodal,&KMMmean,&KMMsigma,&KMMpi,xyrdebug);
 	  CHECKPIX(x,y,"KMM Popularity Contest: (%d,%d) Puni: %g Mean: %f Sigma %f Pi: %f\n",
 		   x,y,Punimodal,KMMmean,KMMsigma,KMMpi);
@@ -1459,4 +1525,5 @@
     bool useVariance, 
     bool safe, 
+    int nminpix,
     bool rejection)
 {
@@ -1633,5 +1700,5 @@
 	    psImageMaskType goodMask = 0; // OR of mask bits in all good input pixels
             combineExtract(&num, &suspect, &badMask, &goodMask, buffer, combinedImage, combinedMask, combinedVariance, input, weights, exps, addVariance, reject, x, y, badMaskBits, suspectMaskBits);
-            combinePixels(combinedImage, combinedMask, combinedVariance, exp, expnum, expweight, num, buffer, x, y, blankMaskBits, badMask, goodMask, safe, totalExpWeight);
+            combinePixels(combinedImage, combinedMask, combinedVariance, exp, expnum, expweight, num, buffer, x, y, blankMaskBits, badMask, goodMask, safe, nminpix, totalExpWeight);
 
             if (iter > 0) {
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/imcombine/pmStack.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/imcombine/pmStack.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/imcombine/pmStack.h	(revision 37066)
@@ -61,4 +61,5 @@
                     bool useVariance,   ///< Use variance values for rejection?
                     bool safe,          ///< Play safe with small numbers of input pixels (mask if N <= 2)?
+		    int nminpix,        ///< Minimum number input per pixel to combine
                     bool rejectInspect  ///< Reject pixels instead of marking them for inspection?
     );
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/imcombine/pmSubtraction.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/imcombine/pmSubtraction.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/imcombine/pmSubtraction.c	(revision 37066)
@@ -37,4 +37,5 @@
 #define USE_KERNEL_ERR                  // Use kernel error image?
 #define NUM_COVAR_POS 5                 // Number of positions for covariance calculation
+//MEH -- this is causing diffim fault 5 -- seems not as robust
 #define USE_LOGFIT_REJECT
 
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/imcombine/pmSubtractionSimple.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/imcombine/pmSubtractionSimple.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/imcombine/pmSubtractionSimple.c	(revision 37066)
@@ -34,4 +34,5 @@
 #include "pmSourceSatstar.h"
 
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/imcombine/pmSubtractionStamps.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/imcombine/pmSubtractionStamps.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/imcombine/pmSubtractionStamps.c	(revision 37066)
@@ -21,4 +21,5 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmSourceMasks.h"
@@ -26,4 +27,5 @@
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/Makefile.am
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/Makefile.am	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/Makefile.am	(revision 37066)
@@ -20,4 +20,5 @@
 	pmModelClass.c \
 	pmModelUtils.c \
+	pmModel_CentralPixel.c \
 	pmSource.c \
 	pmPhotObj.c \
@@ -39,4 +40,5 @@
 	pmSourceIO_SX.c \
 	pmSourceIO_CMP.c \
+	pmSourceIO_CFF.c \
 	pmSourceIO_SMPDATA.c \
 	pmSourceIO_PS1_DEV_0.c \
@@ -47,9 +49,12 @@
 	pmSourceIO_CMF_PS1_V3.c \
 	pmSourceIO_CMF_PS1_V4.c \
+	pmSourceIO_CMF_PS1_V5.c \
 	pmSourceIO_CMF_PS1_SV1.c \
 	pmSourceIO_CMF_PS1_SV2.c \
+	pmSourceIO_CMF_PS1_SV3.c \
 	pmSourceIO_CMF_PS1_DV1.c \
 	pmSourceIO_CMF_PS1_DV2.c \
 	pmSourceIO_CMF_PS1_DV3.c \
+	pmSourceIO_CMF_PS1_DV4.c \
 	pmSourceIO_MatchedRefs.c \
 	pmSourcePlots.c \
@@ -74,4 +79,5 @@
 	pmGrowthCurve.c \
 	pmSourceMatch.c \
+	pmSourceLensing.c \
 	pmDetEff.c \
 	pmSourceGroups.c \
@@ -97,4 +103,5 @@
 	pmModelClass.h \
 	pmModelUtils.h \
+	pmModel_CentralPixel.h \
 	pmSource.h \
 	pmPhotObj.h \
@@ -110,5 +117,5 @@
 	pmSourceOutputs.h \
 	pmSourceIO.h \
-	pmSourceSatstar.h \ 
+	pmSourceSatstar.h \
 	pmSourcePlots.h \
 	pmSourceVisual.h \
@@ -122,4 +129,5 @@
 	pmGrowthCurveGenerate.h \
 	pmSourceMatch.h \
+	pmSourceLensing.h \
 	pmDetEff.h \
 	pmSourceGroups.h \
@@ -142,9 +150,12 @@
 pmSourceIO_CMF_PS1_V3.c \
 pmSourceIO_CMF_PS1_V4.c \
+pmSourceIO_CMF_PS1_V5.c \
 pmSourceIO_CMF_PS1_DV1.c \
 pmSourceIO_CMF_PS1_DV2.c \
 pmSourceIO_CMF_PS1_DV3.c \
+pmSourceIO_CMF_PS1_DV4.c \
 pmSourceIO_CMF_PS1_SV1.c \
-pmSourceIO_CMF_PS1_SV2.c
+pmSourceIO_CMF_PS1_SV2.c \
+pmSourceIO_CMF_PS1_SV3.c
 
 pmSourceIO_CMF_PS1_V1.c : pmSourceIO_CMF.c.in mksource.pl
@@ -160,4 +171,7 @@
 	mksource.pl pmSourceIO_CMF.c.in PS1_V4 pmSourceIO_CMF_PS1_V4.c
 
+pmSourceIO_CMF_PS1_V5.c : pmSourceIO_CMF.c.in mksource.pl
+	mksource.pl pmSourceIO_CMF.c.in PS1_V5 pmSourceIO_CMF_PS1_V5.c
+
 pmSourceIO_CMF_PS1_DV1.c : pmSourceIO_CMF.c.in mksource.pl
 	mksource.pl pmSourceIO_CMF.c.in PS1_DV1 pmSourceIO_CMF_PS1_DV1.c
@@ -169,4 +183,7 @@
 	mksource.pl pmSourceIO_CMF.c.in PS1_DV3 pmSourceIO_CMF_PS1_DV3.c
 
+pmSourceIO_CMF_PS1_DV4.c : pmSourceIO_CMF.c.in mksource.pl
+	mksource.pl pmSourceIO_CMF.c.in PS1_DV4 pmSourceIO_CMF_PS1_DV4.c
+
 pmSourceIO_CMF_PS1_SV1.c : pmSourceIO_CMF.c.in mksource.pl
 	mksource.pl pmSourceIO_CMF.c.in PS1_SV1 pmSourceIO_CMF_PS1_SV1.c
@@ -175,3 +192,6 @@
 	mksource.pl pmSourceIO_CMF.c.in PS1_SV2 pmSourceIO_CMF_PS1_SV2.c
 
+pmSourceIO_CMF_PS1_SV3.c : pmSourceIO_CMF.c.in mksource.pl
+	mksource.pl pmSourceIO_CMF.c.in PS1_SV3 pmSourceIO_CMF_PS1_SV3.c
+
 # EXTRA_DIST = pmErrorCodes.h.in pmErrorCodes.dat pmErrorCodes.c.in
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/mksource.pl
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/mksource.pl	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/mksource.pl	(revision 37066)
@@ -19,11 +19,14 @@
 	       "PS1_V3", 3,
 	       "PS1_V4", 4,
+	       "PS1_V5", 5,
     );
 %cmfmodes_dv = ("PS1_DV1", 1,
 		"PS1_DV2", 2,
 		"PS1_DV3", 3,
+		"PS1_DV4", 4,
     );
 %cmfmodes_sv = ("PS1_SV1", 1,
 		"PS1_SV2", 2,
+		"PS1_SV3", 3,
     );
 
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/fwhm.sh
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/fwhm.sh	(revision 37066)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/fwhm.sh	(revision 37066)
@@ -0,0 +1,218 @@
+
+macro find.fwhm.pgauss
+  if ($0 != 1)
+    echo "USAGE: find.fwhm"
+    break
+  end
+  
+  $dz = 0.01
+  create z 0 50 $dz
+
+  # Gaussian taylor expansion
+  # f = 1 / (1 + z + z^2/2 + z^3/6)
+  # 1 + z + z^2/2 + z^3/6 = 2
+  # f = z + z^2/2 + z^3/6 - 2, find where f == 0.0
+  
+  set f = z + 0.5*z^2 + (1/6.0)*z^3 - 2.0
+  set dfdz = 1 + z + 0.5*z^2
+
+  lim -n 0 z f; clear; box; plot z f
+  lim -n 1 z dfdz; clear; box; plot z dfdz  
+
+  $nZ0 = 0
+  $nZ1 = 5 / $dz
+
+  $Zg = 0.5*(z[$nZ0] + z[$nZ1])  
+  $nZg = int($Zg / $dz)
+  $dZ = 1.0
+
+  for i 0 10
+    $Fg = f[$nZg]
+    $dFdz_g = dfdz[$nZg]
+
+    $dZ = $Fg / $dFdz_g
+
+    echo $Zg $Fg $dZ $dFdz_g
+
+    $Zg -= $dZ
+    $Zg = max ($Zg , 0)
+    $nZg = int($Zg / $dz)
+  end 
+  echo $Zg $Fg $dZ $dFdz_g
+  $Zhm = $Zg
+  $FWHM = 2*sqrt(2*$Zg)
+
+  echo $Zhm : $FWHM
+end
+
+macro find.fwhm.rgauss
+  if ($0 != 2)
+    echo "USAGE: find.fwhm (K)"
+    break
+  end
+  
+  $K = $1
+
+  if ($K == 0.0)
+    $Zhm = (sqrt(5) - 1.0) / 2.0
+    $FWHM = 2*sqrt(2*$Zhm)
+    echo $K : $Zhm : $FWHM
+    return
+  end
+
+  $dz = 0.01
+  create z 0 50 $dz
+
+  # set f = 1.0 / (1.0 + $K*z + z^1.667)
+  # f = $K*z + z^1.667 - 1.0, find where f == 0.0
+  
+  set f = z + z^$K - 1.0
+  set dfdz = ln(z) * z^$K + 1.0
+
+  lim -n 0 z f; clear; box; plot z f
+  lim -n 1 z dfdz; clear; box; plot z dfdz  
+
+  $nZ0 = 0
+  $nZ1 = 5 / $dz
+
+  $Zg = 0.5*(z[$nZ0] + z[$nZ1])  
+  $nZg = int($Zg / $dz)
+  $dZ = 1.0
+
+  for i 0 10
+    $Fg = f[$nZg]
+    $dFdz_g = dfdz[$nZg]
+
+    $dZ = $Fg / $dFdz_g
+
+    echo $nZg $Zg $Fg $dZ $dFdz_g
+
+    $Zg -= $dZ
+    $Zg = max ($Zg , 0)
+    $nZg = int($Zg / $dz)
+  end 
+  # echo $Zg $Fg $dZ $dFdz_g
+  $Zhm = $Zg
+  $FWHM = 2*sqrt(2*$Zg)
+
+  echo $K : $Zhm : $FWHM
+end
+
+macro find.fwhm.ps1v1
+  if ($0 != 2)
+    echo "USAGE: find.fwhm (K)"
+    break
+  end
+  
+  $K = $1
+
+  if ($K == 0.0)
+    $Zhm = 1.0
+    $FWHM = 2*sqrt(2)
+    echo $K : $Zhm : $FWHM
+    return
+  end
+
+  $dz = 0.01
+  create z 0 50 $dz
+
+  # set f = 1.0 / (1.0 + $K*z + z^1.667)
+  # f = $K*z + z^1.667 - 1.0, find where f == 0.0
+  
+  set f = $K*z + z^1.667 - 1.0
+  set dfdz = $K + z^0.667
+
+  #lim -n 0 z f; clear; box; plot z f
+  #lim -n 1 z dfdz; clear; box; plot z dfdz  
+
+  $nZ0 = 0
+  $nZ1 = 5 / $dz
+
+  $Zg = 0.5*(z[$nZ0] + z[$nZ1])  
+  $nZg = int($Zg / $dz)
+  $dZ = 1.0
+
+  for i 0 10
+    $Fg = f[$nZg]
+    $dFdz_g = dfdz[$nZg]
+
+    $dZ = $Fg / $dFdz_g
+
+    # echo $Zg $Fg $dZ $dFdz_g
+
+    $Zg -= $dZ
+    $Zg = max ($Zg , 0)
+    $nZg = int($Zg / $dz)
+  end 
+  # echo $Zg $Fg $dZ $dFdz_g
+  $Zhm = $Zg
+  $FWHM = 2*sqrt(2*$Zg)
+
+  echo $K : $Zhm : $FWHM
+end
+
+# qgauss is like ps1_v1 with z^2.25
+macro find.fwhm.qgauss
+  if ($0 != 2)
+    echo "USAGE: find.qgauss (K)"
+    break
+  end
+  
+  $K = $1
+
+  if ($K == 0.0)
+    $Zhm = 1.0
+    $FWHM = 2*sqrt(2)
+    echo $K : $Zhm : $FWHM
+    return
+  end
+
+  $dz = 0.01
+  create z 0 50 $dz
+
+  # set f = 1.0 / (1.0 + $K*z + z^2.25)
+  # f = $K*z + z^2.25 - 1.0, find where f == 0.0
+  
+  set f = $K*z + z^2.25 - 1.0
+  set dfdz = $K + z^1.25
+
+  #lim -n 0 z f; clear; box; plot z f
+  #lim -n 1 z dfdz; clear; box; plot z dfdz  
+
+  $nZ0 = 0
+  $nZ1 = 5 / $dz
+
+  $Zg = 0.5*(z[$nZ0] + z[$nZ1])  
+  $nZg = int($Zg / $dz)
+  $dZ = 1.0
+
+  for i 0 10
+    $Fg = f[$nZg]
+    $dFdz_g = dfdz[$nZg]
+
+    $dZ = $Fg / $dFdz_g
+
+    # echo $Zg $Fg $dZ $dFdz_g
+
+    $Zg -= $dZ
+    $Zg = max ($Zg , 0)
+    $nZg = int($Zg / $dz)
+  end 
+  # echo $Zg $Fg $dZ $dFdz_g
+  $Zhm = $Zg
+  $FWHM = 2*sqrt(2*$Zg)
+
+  echo $K : $Zhm : $FWHM
+end
+
+macro fwhm.trend
+
+  delete fwhm_v k_v
+
+  for k 0 20 1.0
+    find.fwhm.qgauss $k
+    concat $k k_v
+    concat $FWHM fwhm_v
+  end
+end
+
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_DEV.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_DEV.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_DEV.c	(revision 37066)
@@ -16,5 +16,4 @@
    * 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,
@@ -37,11 +36,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -49,4 +49,5 @@
 #include "pmPSFtry.h"
 #include "pmDetections.h"
+#include "pmModel_CentralPixel.h"
 
 #include "pmModel_DEV.h"
@@ -58,4 +59,5 @@
 # define PM_MODEL_LIMITS          pmModelLimits_DEV
 # define PM_MODEL_RADIUS          pmModelRadius_DEV
+# define PM_MODEL_SET_FWHM        pmModelSetFWHM_DEV
 # define PM_MODEL_FROM_PSF        pmModelFromPSF_DEV
 # define PM_MODEL_PARAMS_FROM_PSF pmModelParamsFromPSF_DEV
@@ -63,7 +65,8 @@
 # define PM_MODEL_SET_LIMITS      pmModelSetLimits_DEV
 
-// f = exp(-z^0.125) 
+// f = exp(-kappa*r^(1/index)) 
+// f = exp(-kappa*z^(0.5/index)) 
+// index = 4, 0.5/index = 0.125
 # define ALPHA 0.125 
-// # define ALPHA 0.25 
 
 // the model is a function of the pixel coordinate (pixcoord[0,1] = x,y)
@@ -73,5 +76,5 @@
 // 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 };
+static float paramsMaxLax[] = { 1.0e5, 1.0e9, 1.0e5, 1.0e5, 100, 100, 1.0 };
 
 // Moderate parameter limits
@@ -86,9 +89,7 @@
 static float *paramsMinUse = paramsMinLax;
 static float *paramsMaxUse = paramsMaxLax;
-static float betaUse[] = { 1000, 3e6, 5, 5, 1.0, 1.0, 0.5 };
+static float betaUse[] = { 2, 3e6, 5, 5, 10.0, 10.0, 0.5 };
 
 static bool limitsApply = true;         // Apply limits?
-
-# include "pmModel_SERSIC.CP.h"
 
 psF32 PM_MODEL_FUNC (psVector *deriv,
@@ -109,85 +110,31 @@
     psAssert (z >= 0, "do not allow negative z values in model");
 
-    float index = 0.5 / ALPHA;
-    float par7 = ALPHA;
-    float bn = 1.9992*index - 0.3271;
-    float Io = exp(bn);
-
-    psF32 f2 = bn*pow(z,ALPHA);
-    psF32 f1 = Io*exp(-f2);
-
-    psF32 radius = hypot(X, Y);
-    if (radius < 1.0) {
-
-	// ** use bilinear interpolation to the given location from the 4 surrounding pixels centered on the object center
-
-	// first, use Rmajor and index to find the central pixel flux (fraction of total flux)
-	psEllipseAxes axes;
-	pmModelParamsToAxes (&axes, PAR[PM_PAR_SXX], PAR[PM_PAR_SXY], PAR[PM_PAR_SYY], true);
-
-	// get the central pixel flux from the lookup table
-	float xPix = (axes.major - centralPixelXo) / centralPixeldX;
-	xPix = PS_MIN (PS_MAX(xPix, 0), centralPixelNX - 1);
-	float yPix = (index - centralPixelYo) / centralPixeldY;
-	yPix = PS_MIN (PS_MAX(yPix, 0), centralPixelNY - 1);
-
-	// the integral of a Sersic has an analytical form as follows:
-	float logGamma = lgamma(2.0*index);
-	float bnFactor = pow(bn, 2.0*index);
-	float norm = 2.0 * M_PI * PS_SQR(axes.major) * index * exp(bn) * exp(logGamma) / bnFactor;
-
-	// XXX interpolate to get the value
-	// XXX for the moment, just integerize
-	// XXX I need to multiply by the integrated flux to get the flux in the central pixel
-	float Vcenter = centralPixel[(int)yPix][(int)xPix] * norm;
-	
-	float px1 = 1.0 / PAR[PM_PAR_SXX];
-	float py1 = 1.0 / PAR[PM_PAR_SYY];
-	float z10 = PS_SQR(px1);
-	float z01 = PS_SQR(py1);
-
-	// which pixels do we need for this interpolation?
-	// (I do not keep state information, so I don't know anything about other evaluations of nearby pixels...)
-	if ((X >= 0) && (Y >= 0)) {
-	    float z11 = z10 + z01 + PAR[PM_PAR_SXY]; // X * Y positive
-	    float V00 = Vcenter;
-	    float V10 = Io*exp(-bn*pow(z10,par7));
-	    float V01 = Io*exp(-bn*pow(z01,par7));
-	    float V11 = Io*exp(-bn*pow(z11,par7));
-	    f1 = interpolatePixels(V00, V10, V01, V11, X, Y);
-	}
-	if ((X < 0) && (Y >= 0)) {
-	    float z11 = z10 + z01 - PAR[PM_PAR_SXY]; // X * Y negative
-	    float V00 = Io*exp(-bn*pow(z10,par7));
-	    float V10 = Vcenter;
-	    float V01 = Io*exp(-bn*pow(z11,par7));
-	    float V11 = Io*exp(-bn*pow(z01,par7));
-	    f1 = interpolatePixels(V00, V10, V01, V11, (1.0 + X), Y);
-	}
-	if ((X >= 0) && (Y < 0)) {
-	    float z11 = z10 + z01 - PAR[PM_PAR_SXY]; // X * Y negative
-	    float V00 = Io*exp(-bn*pow(z01,par7));
-	    float V10 = Io*exp(-bn*pow(z11,par7));
-	    float V01 = Vcenter;
-	    float V11 = Io*exp(-bn*pow(z10,par7));
-	    f1 = interpolatePixels(V00, V10, V01, V11, X, (1.0 + Y));
-	}
-	if ((X < 0) && (Y < 0)) {
-	    float z11 = z10 + z01 + PAR[PM_PAR_SXY]; // X * Y positive
-	    float V00 = Io*exp(-bn*pow(z11,par7));
-	    float V10 = Io*exp(-bn*pow(z10,par7));
-	    float V01 = Io*exp(-bn*pow(z01,par7));
-	    float V11 = Vcenter;
-	    f1 = interpolatePixels(V00, V10, V01, V11, (1.0 + X), (1.0 + Y));
-	}
+    // for DEV, we can hard-wire kappa(4):
+    // float index = 4.0;
+    float kappa = 7.670628;
+
+    // r = sqrt(z)
+    float q = kappa*pow(z,ALPHA);
+    float f0 = exp(-q);
+
+    assert (isfinite(q));
+
+    // only worry about the central pixels at most
+    float radius = hypot(X, Y);
+    if (radius <= 1.5) {
+	// Nsub ~ 10*index^2 + 1
+	psEllipseAxes axes = pmPSF_ModelToAxes(PAR, true); // DEV uses Reff
+	int Nsub = 2 * ((int)(25 / axes.minor)) + 1;
+	Nsub = PS_MIN (Nsub, 121);
+	Nsub = PS_MAX (Nsub, 11);
+	f0 = pmModelCP_SersicSubpix (X, Y, PAR[PM_PAR_SXX], PAR[PM_PAR_SXY], PAR[PM_PAR_SYY], 4.0, Nsub);
     }   
-
-    psF32 z0 = PAR[PM_PAR_I0]*f1;
-    psF32 f0 = PAR[PM_PAR_SKY] + z0;
-
-    assert (isfinite(f2));
+    assert (isfinite(f0));
+
+    float f1 = PAR[PM_PAR_I0]*f0;
+    float f = PAR[PM_PAR_SKY] + f1;
+
     assert (isfinite(f1));
-    assert (isfinite(z0));
-    assert (isfinite(f0));
+    assert (isfinite(f));
 
     if (deriv != NULL) {
@@ -195,18 +142,24 @@
 
         dPAR[PM_PAR_SKY]  = +1.0;
-        dPAR[PM_PAR_I0]   = +2.0*f1; // XXX extra damping..
-
-        // 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);
-
-        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);
+        dPAR[PM_PAR_I0]   = +f0;
+
+	if (z > 0.01) {
+	  float z1 = f1*kappa*ALPHA*pow(z,ALPHA-1.0);
+	  dPAR[PM_PAR_XPOS] = +1.0*z1*(2.0*px + Y*PAR[PM_PAR_SXY]);
+	  dPAR[PM_PAR_YPOS] = +1.0*z1*(2.0*py + 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;
+	} else {
+	  // gradient -> 0 for z -> 0, but has undef form
+	  float z1 = f1*kappa*ALPHA*pow(z,ALPHA);
+	  dPAR[PM_PAR_XPOS] = +1.0*z1*(2.0/PAR[PM_PAR_SXX] + PAR[PM_PAR_SXY]);
+	  dPAR[PM_PAR_YPOS] = +1.0*z1*(2.0/PAR[PM_PAR_SYY] + PAR[PM_PAR_SXY]);
+	  dPAR[PM_PAR_SXX]  = +2.0*z1*px/PAR[PM_PAR_SXX]/PAR[PM_PAR_SXX];
+	  dPAR[PM_PAR_SYY]  = +2.0*z1*py/PAR[PM_PAR_SYY]/PAR[PM_PAR_SYY];
+	  dPAR[PM_PAR_SXY]  = -1.0*z1;
+	}
+    }
+    return (f);
 }
 
@@ -292,4 +245,12 @@
 bool PM_MODEL_GUESS (pmModel *model, pmSource *source, psImageMaskType maskVal, psImageMaskType markVal)
 {
+    // for the moment, we are going to require moments and KronFlux
+    if (!source->moments) return false;
+    pmMoments *moments = source->moments;
+
+    if (!isfinite(moments->KronFlux)) return false;
+    if (!isfinite(moments->Mrf)) return false;
+    if (moments->Mrf < 0.0) return false;
+
     psF32 *PAR  = model->params->data.F32;
 
@@ -297,19 +258,32 @@
     PAR[PM_PAR_SKY]  = 0.0;
 
-    // set the shape parameters
-    if (!pmModelSetShape(&PAR[PM_PAR_SXX], &PAR[PM_PAR_SXY], &PAR[PM_PAR_SYY], source->moments, true)) {
-      return false;
-    }
-
-    // the normalization is modified by the slope
-    float index = 0.5 / ALPHA;
-    float bn = 1.9992*index - 0.3271;
-    float Io = exp(0.5*bn);
-
-    // set the model normalization
-    if (!pmModelSetNorm(&PAR[PM_PAR_I0], source)) {
-      return false;
-    }
-    PAR[PM_PAR_I0] /= Io;
+    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;
+
+    // Mxx, Mxy, Myy define the elliptical shape, but Mrf defines the width 
+    // the factor of 2.3 comes from Table 1 of Graham and Driver (2005)
+    float scale = moments->Mrf / axes.major / 2.3;
+    axes.major *= scale;
+    axes.minor *= scale;
+
+    pmModelAxesToParams (&PAR[PM_PAR_SXX], &PAR[PM_PAR_SXY], &PAR[PM_PAR_SYY], axes, true);
+
+    // psEllipseAxes axes;
+    // use the code in SetShape here to avoid doing this 2x
+    // pmModelParamsToAxes (&axes, PAR[PM_PAR_SXX], PAR[PM_PAR_SXY], PAR[PM_PAR_SYY], true);
+
+    // float norm = pmSersicNorm (4);  // hardwire
+    float norm = 0.00168012;
+    float normFlux = 2.0 * M_PI * axes.major * axes.minor * norm;
+    PAR[PM_PAR_I0] = moments->KronFlux / normFlux;
 
     // set the model position
@@ -328,17 +302,9 @@
     psEllipseAxes axes;
     pmModelParamsToAxes (&axes, PAR[PM_PAR_SXX], PAR[PM_PAR_SXY], PAR[PM_PAR_SYY], true);
-    float AspectRatio = axes.minor / axes.major;
-
-    float index = 4.0;
-    float bn = 1.9992*index - 0.3271;
-
-    // the integral of a Sersic has an analytical form as follows:
-    float logGamma = lgamma(2.0*index);
-    float bnFactor = pow(bn, 2.0*index);
-    float norm = 2.0 * M_PI * PS_SQR(axes.major) * index * exp(bn) * exp(logGamma) / bnFactor;
-    
-    psF64 Flux = PAR[PM_PAR_I0] * norm * AspectRatio;
-
-    return(Flux);
+
+    float norm = 0.00168012;
+    float flux = PAR[PM_PAR_I0] * 2.0 * M_PI * axes.major * axes.minor * norm;
+
+    return(flux);
 }
 
@@ -359,11 +325,18 @@
     pmModelParamsToAxes (&axes, PAR[PM_PAR_SXX], PAR[PM_PAR_SXY], PAR[PM_PAR_SYY], true);
 
-    // 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);
+    // static value for DEV:
+    float kappa = 7.670628;
+
+    // f = Io exp(-kappa*z^n) -> z^n = ln(Io/f) / kappa
+    psF64 zn = log(PAR[PM_PAR_I0] / flux) / kappa;
+    psF64 radius = axes.major * pow(zn, 0.5 / ALPHA);
 
     psAssert (isfinite(radius), "fix this code: radius should not be nan for Io = %f, flux = %f, major = %f (%f, %f, %f)", 
 	      PAR[PM_PAR_I0], flux, axes.major, PAR[PM_PAR_SXX], PAR[PM_PAR_SXY], PAR[PM_PAR_SYY]);
     return (radius);
+}
+
+psF64 PM_MODEL_SET_FWHM (const psVector *params, psF64 sigma) {
+  return (NAN);
 }
 
@@ -389,5 +362,5 @@
     // the 2D PSF model fits polarization terms (E0,E1,E2)
     // convert to shape terms (SXX,SYY,SXY)
-    bool useReff = pmModelUseReff (modelPSF->type);
+    bool useReff = modelPSF->class->useReff;
     if (!pmPSF_FitToModel (out, 0.1, useReff)) {
         psTrace("psModules.objects", 5, "Failed to fit object at (r,c) = (%.1f,%.1f)", in[PM_PAR_YPOS], in[PM_PAR_XPOS]);
@@ -443,5 +416,5 @@
     // convert to shape terms (SXX,SYY,SXY)
     // XXX user-defined value for limit?
-    bool useReff = pmModelUseReff (model->type);
+    bool useReff = model->class->useReff;
     if (!pmPSF_FitToModel (PAR, 0.1, useReff)) {
         psTrace ("psModules.objects", 3, "Failed to fit object at (r,c) = (%.1f,%.1f)", Xo, Yo);
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_DEV.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_DEV.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_DEV.h	(revision 37066)
@@ -8,4 +8,5 @@
 psF64 pmModelFlux_DEV(const psVector *params);
 psF64 pmModelRadius_DEV(const psVector *params, psF64 flux);
+psF64 pmModelSetFWHM_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);
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_EXP.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_EXP.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_EXP.c	(revision 37066)
@@ -33,11 +33,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -45,4 +46,5 @@
 #include "pmPSFtry.h"
 #include "pmDetections.h"
+#include "pmModel_CentralPixel.h"
 
 #include "pmModel_EXP.h"
@@ -54,4 +56,5 @@
 # define PM_MODEL_LIMITS          pmModelLimits_EXP
 # define PM_MODEL_RADIUS          pmModelRadius_EXP
+# define PM_MODEL_SET_FWHM        pmModelSetFWHM_EXP
 # define PM_MODEL_FROM_PSF        pmModelFromPSF_EXP
 # define PM_MODEL_PARAMS_FROM_PSF pmModelParamsFromPSF_EXP
@@ -62,8 +65,21 @@
 // 0.5 PIX: the parameters are defined in terms of pixel coords, so the incoming pixcoords
 // values need to be pixel coords
+//
+
+// Notes on changing kappa value from 1.70056 to 1.678
+// I'm using a functional form f(x,y) = Io exp(-kappa (r / r_e)).  
+// The article by Graham & Driver (2005) uses a form Ie exp(-bn [(r / r_e) -1]) 
+// which is equal to Ie exp(-bn (r / r_e)) exp(bn).  
+// Thus, my Io = Ie exp(bn) and my kappa is their bn.
+// My value of kappa is 1.700, their value for bn is 1.678., so I am off by a small amount there (1.5%).  
+
+
+#define KAPPA_EXP 1.678
+#define OLD_KAPP_EXP 1.70056
+
 
 // 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 };
+static float paramsMaxLax[] = { 1.0e5, 1.0e9, 1.0e5, 1.0e5, 100, 100, 1.0 };
 
 // Moderate parameter limits
@@ -78,9 +94,14 @@
 static float *paramsMinUse = paramsMinLax;
 static float *paramsMaxUse = paramsMaxLax;
-static float betaUse[] = { 1000, 3e6, 5, 5, 1.0, 1.0, 0.5};
+static float betaUse[] = { 2, 3e6, 5, 5, 10.0, 10.0, 0.5};
 
 static bool limitsApply = true;         // Apply limits?
 
-# include "pmModel_SERSIC.CP.h"
+// # include "pmModel_SERSIC.CP.h"
+
+// the problems I'm having with the SERSIC-like functions are:
+// 1) making sure I have the right functional form so that PAR[SXX,etc] represent R_eff (half-light radius)
+// 2) getting the central pixel right
+// 3) getting the derivaties right.
 
 psF32 PM_MODEL_FUNC (psVector *deriv,
@@ -101,85 +122,26 @@
     psAssert (z >= 0, "do not allow negative z values in model");
 
-    float index = 1.0;
-    float par7 = 0.5;
-    float bn = 1.9992*index - 0.3271;
-    float Io = exp(bn);
-
-    psF32 f2 = bn*sqrt(z);
-    psF32 f1 = Io*exp(-f2);
-
+    // for EXP, we can hard-wire kappa(1):
+    // float index = 1.0;
+    float kappa = KAPPA_EXP;
+
+    // sqrt(z) is r
+    float q = kappa*sqrt(z);
+    psF32 f0 = exp(-q);
+
+    assert (isfinite(q));
+
+    // only worry about the central 4 pixels at most
     psF32 radius = hypot(X, Y);
-    if (radius < 1.0) {
-
-	// ** use bilinear interpolation to the given location from the 4 surrounding pixels centered on the object center
-
-	// first, use Rmajor and index to find the central pixel flux (fraction of total flux)
-	psEllipseAxes axes;
-	pmModelParamsToAxes (&axes, PAR[PM_PAR_SXX], PAR[PM_PAR_SXY], PAR[PM_PAR_SYY], true);
-
-	// get the central pixel flux from the lookup table
-	float xPix = (axes.major - centralPixelXo) / centralPixeldX;
-	xPix = PS_MIN (PS_MAX(xPix, 0), centralPixelNX - 1);
-	float yPix = (index - centralPixelYo) / centralPixeldY;
-	yPix = PS_MIN (PS_MAX(yPix, 0), centralPixelNY - 1);
-
-	// the integral of a Sersic has an analytical form as follows:
-	float logGamma = lgamma(2.0*index);
-	float bnFactor = pow(bn, 2.0*index);
-	float norm = 2.0 * M_PI * PS_SQR(axes.major) * index * exp(bn) * exp(logGamma) / bnFactor;
-
-	// XXX interpolate to get the value
-	// XXX for the moment, just integerize
-	// XXX I need to multiply by the integrated flux to get the flux in the central pixel
-	float Vcenter = centralPixel[(int)yPix][(int)xPix] * norm;
-	
-	float px1 = 1.0 / PAR[PM_PAR_SXX];
-	float py1 = 1.0 / PAR[PM_PAR_SYY];
-	float z10 = PS_SQR(px1);
-	float z01 = PS_SQR(py1);
-
-	// which pixels do we need for this interpolation?
-	// (I do not keep state information, so I don't know anything about other evaluations of nearby pixels...)
-	if ((X >= 0) && (Y >= 0)) {
-	    float z11 = z10 + z01 + PAR[PM_PAR_SXY]; // X * Y positive
-	    float V00 = Vcenter;
-	    float V10 = Io*exp(-bn*pow(z10,par7));
-	    float V01 = Io*exp(-bn*pow(z01,par7));
-	    float V11 = Io*exp(-bn*pow(z11,par7));
-	    f1 = interpolatePixels(V00, V10, V01, V11, X, Y);
-	}
-	if ((X < 0) && (Y >= 0)) {
-	    float z11 = z10 + z01 - PAR[PM_PAR_SXY]; // X * Y negative
-	    float V00 = Io*exp(-bn*pow(z10,par7));
-	    float V10 = Vcenter;
-	    float V01 = Io*exp(-bn*pow(z11,par7));
-	    float V11 = Io*exp(-bn*pow(z01,par7));
-	    f1 = interpolatePixels(V00, V10, V01, V11, (1.0 + X), Y);
-	}
-	if ((X >= 0) && (Y < 0)) {
-	    float z11 = z10 + z01 - PAR[PM_PAR_SXY]; // X * Y negative
-	    float V00 = Io*exp(-bn*pow(z01,par7));
-	    float V10 = Io*exp(-bn*pow(z11,par7));
-	    float V01 = Vcenter;
-	    float V11 = Io*exp(-bn*pow(z10,par7));
-	    f1 = interpolatePixels(V00, V10, V01, V11, X, (1.0 + Y));
-	}
-	if ((X < 0) && (Y < 0)) {
-	    float z11 = z10 + z01 + PAR[PM_PAR_SXY]; // X * Y positive
-	    float V00 = Io*exp(-bn*pow(z11,par7));
-	    float V10 = Io*exp(-bn*pow(z10,par7));
-	    float V01 = Io*exp(-bn*pow(z01,par7));
-	    float V11 = Vcenter;
-	    f1 = interpolatePixels(V00, V10, V01, V11, (1.0 + X), (1.0 + Y));
-	}
-    }   
-
-    psF32 z0 = PAR[PM_PAR_I0]*f1;
-    psF32 f0 = PAR[PM_PAR_SKY] + z0;
-
-    assert (isfinite(f2));
+    if (radius <= 1.5) {
+	f0 = pmModelCP_SersicSubpix (X, Y, PAR[PM_PAR_SXX], PAR[PM_PAR_SXY], PAR[PM_PAR_SYY], 1.0, 51);
+    }
+    assert (isfinite(f0));
+
+    psF32 f1 = PAR[PM_PAR_I0]*f0;
+    psF32 f = PAR[PM_PAR_SKY] + f1;
+
     assert (isfinite(f1));
-    assert (isfinite(z0));
-    assert (isfinite(f0));
+    assert (isfinite(f));
 
     if (deriv != NULL) {
@@ -187,18 +149,24 @@
 
         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*bn*z0/sqrt(0.01) : 0.5*bn*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);
+        dPAR[PM_PAR_I0]   = +f0;
+
+	if (z > 0.01) {
+	  float z1 = 0.5*f1*kappa/sqrt(z);
+	  dPAR[PM_PAR_XPOS] = +1.0*z1*(2.0*px + Y*PAR[PM_PAR_SXY]);
+	  dPAR[PM_PAR_YPOS] = +1.0*z1*(2.0*py + 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;
+	} else {
+	  // gradient -> 0 for z -> 0, but has undef form
+	  float z1 = 0.5*f1*kappa;
+	  dPAR[PM_PAR_XPOS] = +1.0*z1*(2.0/PAR[PM_PAR_SXX] + PAR[PM_PAR_SXY]);
+	  dPAR[PM_PAR_YPOS] = +1.0*z1*(2.0/PAR[PM_PAR_SYY] + PAR[PM_PAR_SXY]);
+	  dPAR[PM_PAR_SXX]  = +2.0*z1*px/PAR[PM_PAR_SXX]/PAR[PM_PAR_SXX];
+	  dPAR[PM_PAR_SYY]  = +2.0*z1*py/PAR[PM_PAR_SYY]/PAR[PM_PAR_SYY];
+	  dPAR[PM_PAR_SXY]  = -1.0*z1;
+	}
+    }
+    return (f);
 }
 
@@ -284,4 +252,12 @@
 bool PM_MODEL_GUESS (pmModel *model, pmSource *source, psImageMaskType maskVal, psImageMaskType markVal)
 {
+    // for the moment, we are going to require moments and KronFlux
+    if (!source->moments) return false;
+    pmMoments *moments = source->moments;
+
+    if (!isfinite(moments->KronFlux)) return false;
+    if (!isfinite(moments->Mrf)) return false;
+    if (moments->Mrf < 0.0) return false;
+
     psF32 *PAR  = model->params->data.F32;
 
@@ -289,13 +265,31 @@
     PAR[PM_PAR_SKY]  = 0.0;
 
-    // set the shape parameters
-    if (!pmModelSetShape(&PAR[PM_PAR_SXX], &PAR[PM_PAR_SXY], &PAR[PM_PAR_SYY], source->moments, true)) {
-      return false;
-    }
-
-    // set the model normalization
-    if (!pmModelSetNorm(&PAR[PM_PAR_I0], source)) {
-      return false;
-    }
+    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;
+
+    // Mxx, Mxy, Myy define the elliptical shape, but Mrf defines the width 
+    float scale = moments->Mrf / axes.major;
+    axes.major *= scale;
+    axes.minor *= scale;
+
+    pmModelAxesToParams (&PAR[PM_PAR_SXX], &PAR[PM_PAR_SXY], &PAR[PM_PAR_SYY], axes, true);
+
+    // psEllipseAxes axes;
+    // use the code in SetShape here to avoid doing this 2x
+    // pmModelParamsToAxes (&axes, PAR[PM_PAR_SXX], PAR[PM_PAR_SXY], PAR[PM_PAR_SYY], true);
+
+    // float norm = pmSersicNorm (4);  // hardwire
+    float norm = 0.34578;
+    float normFlux = 2.0 * M_PI * axes.major * axes.minor * norm;
+    PAR[PM_PAR_I0] = moments->KronFlux / normFlux;
 
     // set the model position
@@ -306,5 +300,4 @@
     return(true);
 }
-
 // An exponential model is equivalent to a Sersic with index = 1.0
 psF64 PM_MODEL_FLUX (const psVector *params)
@@ -314,17 +307,11 @@
     psEllipseAxes axes;
     pmModelParamsToAxes (&axes, PAR[PM_PAR_SXX], PAR[PM_PAR_SXY], PAR[PM_PAR_SYY], true);
-    float AspectRatio = axes.minor / axes.major;
-
-    float index = 1.0;
-    float bn = 1.9992*index - 0.3271;
-
-    // the integral of a Sersic has an analytical form as follows:
-    float logGamma = lgamma(2.0*index);
-    float bnFactor = pow(bn, 2.0*index);
-    float norm = 2.0 * M_PI * PS_SQR(axes.major) * index * exp(bn) * exp(logGamma) / bnFactor;
-    
-    psF64 Flux = PAR[PM_PAR_I0] * norm * AspectRatio;
-
-    return(Flux);
+
+    // static value for EXP:
+    float norm = 0.34578; // \int exp(-kappa*sqrt(z)) r dr
+
+    float flux = PAR[PM_PAR_I0] * 2.0 * M_PI * axes.major * axes.minor * norm;
+
+    return(flux);
 }
 
@@ -345,11 +332,18 @@
     pmModelParamsToAxes (&axes, PAR[PM_PAR_SXX], PAR[PM_PAR_SXY], PAR[PM_PAR_SYY], true);
 
-    // 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;
+    // static value for EXP:
+    float kappa = KAPPA_EXP;
+
+    // f = Io exp(-kappa*sqrt(z)) -> sqrt(z) = ln(Io/f) / kappa
+    psF64 zn = log(PAR[PM_PAR_I0] / flux) / kappa;
+    psF64 radius = axes.major * zn;
 
     psAssert (isfinite(radius), "fix this code: radius should not be nan for Io = %f, flux = %f, major = %f (%f, %f, %f)", 
 	      PAR[PM_PAR_I0], flux, axes.major, PAR[PM_PAR_SXX], PAR[PM_PAR_SXY], PAR[PM_PAR_SYY]);
     return (radius);
+}
+
+psF64 PM_MODEL_SET_FWHM (const psVector *params, psF64 sigma) {
+  return (NAN);
 }
 
@@ -375,5 +369,5 @@
     // the 2D PSF model fits polarization terms (E0,E1,E2)
     // convert to shape terms (SXX,SYY,SXY)
-    bool useReff = pmModelUseReff (modelPSF->type);
+    bool useReff = modelPSF->class->useReff;
     if (!pmPSF_FitToModel (out, 0.1, useReff)) {
         psTrace("psModules.objects", 5, "Failed to fit object at (r,c) = (%.1f,%.1f)", in[PM_PAR_YPOS], in[PM_PAR_XPOS]);
@@ -429,5 +423,5 @@
     // convert to shape terms (SXX,SYY,SXY)
     // XXX user-defined value for limit?
-    bool useReff = pmModelUseReff (model->type);
+    bool useReff = model->class->useReff;
     if (!pmPSF_FitToModel (PAR, 0.1, useReff)) {
         psTrace ("psModules.objects", 3, "Failed to fit object at (r,c) = (%.1f,%.1f)", Xo, Yo);
@@ -501,2 +495,66 @@
     return;
 }
+
+# if (0)
+void bilin_inter_function () {
+	// first, use Rmajor and index to find the central pixel flux (fraction of total flux)
+	psEllipseAxes axes;
+	pmModelParamsToAxes (&axes, PAR[PM_PAR_SXX], PAR[PM_PAR_SXY], PAR[PM_PAR_SYY], true);
+
+	// get the central pixel flux from the lookup table
+	float xPix = (axes.major - centralPixelXo) / centralPixeldX;
+	xPix = PS_MIN (PS_MAX(xPix, 0), centralPixelNX - 1);
+	float yPix = (index - centralPixelYo) / centralPixeldY;
+	yPix = PS_MIN (PS_MAX(yPix, 0), centralPixelNY - 1);
+
+	// the integral of a Sersic has an analytical form as follows:
+	float logGamma = lgamma(2.0*index);
+	float bnFactor = pow(bn, 2.0*index);
+	float norm = 2.0 * M_PI * PS_SQR(axes.major) * index * exp(bn) * exp(logGamma) / bnFactor;
+
+	// XXX interpolate to get the value
+	// XXX for the moment, just integerize
+	// XXX I need to multiply by the integrated flux to get the flux in the central pixel
+	float Vcenter = centralPixel[(int)yPix][(int)xPix] * norm;
+	
+	float px1 = 1.0 / PAR[PM_PAR_SXX];
+	float py1 = 1.0 / PAR[PM_PAR_SYY];
+	float z10 = PS_SQR(px1);
+	float z01 = PS_SQR(py1);
+
+	// which pixels do we need for this interpolation?
+	// (I do not keep state information, so I don't know anything about other evaluations of nearby pixels...)
+	if ((X >= 0) && (Y >= 0)) {
+	    float z11 = z10 + z01 + PAR[PM_PAR_SXY]; // X * Y positive
+	    float V00 = Vcenter;
+	    float V10 = Io*exp(-bn*pow(z10,par7));
+	    float V01 = Io*exp(-bn*pow(z01,par7));
+	    float V11 = Io*exp(-bn*pow(z11,par7));
+	    f1 = interpolatePixels(V00, V10, V01, V11, X, Y);
+	}
+	if ((X < 0) && (Y >= 0)) {
+	    float z11 = z10 + z01 - PAR[PM_PAR_SXY]; // X * Y negative
+	    float V00 = Io*exp(-bn*pow(z10,par7));
+	    float V10 = Vcenter;
+	    float V01 = Io*exp(-bn*pow(z11,par7));
+	    float V11 = Io*exp(-bn*pow(z01,par7));
+	    f1 = interpolatePixels(V00, V10, V01, V11, (1.0 + X), Y);
+	}
+	if ((X >= 0) && (Y < 0)) {
+	    float z11 = z10 + z01 - PAR[PM_PAR_SXY]; // X * Y negative
+	    float V00 = Io*exp(-bn*pow(z01,par7));
+	    float V10 = Io*exp(-bn*pow(z11,par7));
+	    float V01 = Vcenter;
+	    float V11 = Io*exp(-bn*pow(z10,par7));
+	    f1 = interpolatePixels(V00, V10, V01, V11, X, (1.0 + Y));
+	}
+	if ((X < 0) && (Y < 0)) {
+	    float z11 = z10 + z01 + PAR[PM_PAR_SXY]; // X * Y positive
+	    float V00 = Io*exp(-bn*pow(z11,par7));
+	    float V10 = Io*exp(-bn*pow(z10,par7));
+	    float V01 = Io*exp(-bn*pow(z01,par7));
+	    float V11 = Vcenter;
+	    f1 = interpolatePixels(V00, V10, V01, V11, (1.0 + X), (1.0 + Y));
+	}
+}
+# endif
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_EXP.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_EXP.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_EXP.h	(revision 37066)
@@ -8,4 +8,5 @@
 psF64 pmModelFlux_EXP(const psVector *params);
 psF64 pmModelRadius_EXP(const psVector *params, psF64 flux);
+psF64 pmModelSetFWHM_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);
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_GAUSS.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_GAUSS.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_GAUSS.c	(revision 37066)
@@ -33,11 +33,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -53,4 +54,5 @@
 # define PM_MODEL_GUESS           pmModelGuess_GAUSS
 # define PM_MODEL_LIMITS          pmModelLimits_GAUSS
+# define PM_MODEL_SET_FWHM        pmModelSetFWHM_GAUSS
 # define PM_MODEL_RADIUS          pmModelRadius_GAUSS
 # define PM_MODEL_FROM_PSF        pmModelFromPSF_GAUSS
@@ -61,5 +63,5 @@
 // Lax parameter limits
 static float paramsMinLax[] = { -1.0e3, 1.0e-2, -100, -100, 0.5, 0.5, -1.0 };
-static float paramsMaxLax[] = { 1.0e5, 1.0e8, 1.0e4, 1.0e4, 100, 100, 1.0 };
+static float paramsMaxLax[] = { 1.0e5, 1.0e9, 1.0e5, 1.0e5, 100, 100, 1.0 };
 
 // Moderate parameter limits
@@ -256,4 +258,8 @@
 }
 
+psF64 PM_MODEL_SET_FWHM (const psVector *params, psF64 sigma) {
+    return (2.35482004503*sigma);
+}
+
 // construct the PSF model from the FLT model and the psf
 bool PM_MODEL_FROM_PSF (pmModel *modelPSF, pmModel *modelFLT, const pmPSF *psf)
@@ -278,5 +284,5 @@
     // the 2D PSF model fits polarization terms (E0,E1,E2)
     // convert to shape terms (SXX,SYY,SXY)
-    bool useReff = pmModelUseReff (modelPSF->type);
+    bool useReff = modelPSF->class->useReff;
     if (!pmPSF_FitToModel (out, 0.1, useReff)) {
         psTrace ("psModules.objects", 3, "Failed to fit object at (r,c) = (%.1f,%.1f)", in[PM_PAR_YPOS], in[PM_PAR_XPOS]);
@@ -330,5 +336,5 @@
     // the 2D PSF model fits polarization terms (E0,E1,E2)
     // convert to shape terms (SXX,SYY,SXY)
-    bool useReff = pmModelUseReff (model->type);
+    bool useReff = model->class->useReff;
     if (!pmPSF_FitToModel (PAR, 0.1, useReff)) {
         psTrace ("psModules.objects", 3, "Failed to fit object at (r,c) = (%.1f,%.1f)", Xo, Yo);
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_GAUSS.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_GAUSS.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_GAUSS.h	(revision 37066)
@@ -8,4 +8,5 @@
 psF64 pmModelFlux_GAUSS(const psVector *params);
 psF64 pmModelRadius_GAUSS(const psVector *params, psF64 flux);
+psF64 pmModelSetFWHM_GAUSS(const psVector *params, psF64 flux);
 bool pmModelFromPSF_GAUSS(pmModel *modelPSF, pmModel *modelFLT, const pmPSF *psf);
 bool  pmModelParamsFromPSF_GAUSS(pmModel *model, const pmPSF *psf, float Xo, float Yo, float Io);
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_PGAUSS.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_PGAUSS.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_PGAUSS.c	(revision 37066)
@@ -33,11 +33,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -54,4 +55,5 @@
 # define PM_MODEL_LIMITS          pmModelLimits_PGAUSS
 # define PM_MODEL_RADIUS          pmModelRadius_PGAUSS
+# define PM_MODEL_SET_FWHM        pmModelSetFWHM_PGAUSS
 # define PM_MODEL_FROM_PSF        pmModelFromPSF_PGAUSS
 # define PM_MODEL_PARAMS_FROM_PSF pmModelParamsFromPSF_PGAUSS
@@ -61,5 +63,5 @@
 // Lax parameter limits
 static float paramsMinLax[] = { -1.0e3, 1.0e-2, -100, -100, 0.5, 0.5, -1.0 };
-static float paramsMaxLax[] = { 1.0e5, 1.0e8, 1.0e4, 1.0e4, 100, 100, 1.0 };
+static float paramsMaxLax[] = { 1.0e5, 1.0e9, 1.0e5, 1.0e5, 100, 100, 1.0 };
 
 // Moderate parameter limits
@@ -323,4 +325,9 @@
 }
 
+// scale factor is constant for PGAUSS, I found it with the fwhm.sh script
+psF64 PM_MODEL_SET_FWHM (const psVector *params, psF64 sigma) {
+    return (3.0063103*sigma);
+}
+
 bool PM_MODEL_FROM_PSF (pmModel *modelPSF, pmModel *modelFLT, const pmPSF *psf)
 {
@@ -343,5 +350,5 @@
     // the 2D PSF model fits polarization terms (E0,E1,E2)
     // convert to shape terms (SXX,SYY,SXY)
-    bool useReff = pmModelUseReff (modelPSF->type);
+    bool useReff = modelPSF->class->useReff;
     if (!pmPSF_FitToModel (out, 0.1, useReff)) {
         psTrace("psModules.objects", 5, "Failed to fit object at (r,c) = (%.1f,%.1f)", in[PM_PAR_YPOS], in[PM_PAR_XPOS]);
@@ -395,5 +402,5 @@
     // the 2D PSF model fits polarization terms (E0,E1,E2)
     // convert to shape terms (SXX,SYY,SXY)
-    bool useReff = pmModelUseReff (model->type);
+    bool useReff = model->class->useReff;
     if (!pmPSF_FitToModel (PAR, 0.1, useReff)) {
         psTrace ("psModules.objects", 3, "Failed to fit object at (r,c) = (%.1f,%.1f)", Xo, Yo);
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_PGAUSS.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_PGAUSS.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_PGAUSS.h	(revision 37066)
@@ -8,4 +8,5 @@
 psF64 pmModelFlux_PGAUSS(const psVector *params);
 psF64 pmModelRadius_PGAUSS(const psVector *params, psF64 flux);
+psF64 pmModelSetFWHM_PGAUSS(const psVector *params, psF64 flux);
 bool pmModelFromPSF_PGAUSS(pmModel *modelPSF, pmModel *modelFLT, const pmPSF *psf);
 bool  pmModelParamsFromPSF_PGAUSS(pmModel *model, const pmPSF *psf, float Xo, float Yo, float Io);
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_PS1_V1.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_PS1_V1.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_PS1_V1.c	(revision 37066)
@@ -35,11 +35,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -56,4 +57,5 @@
 # define PM_MODEL_LIMITS          pmModelLimits_PS1_V1
 # define PM_MODEL_RADIUS          pmModelRadius_PS1_V1
+# define PM_MODEL_SET_FWHM        pmModelSetFWHM_PS1_V1
 # define PM_MODEL_FROM_PSF        pmModelFromPSF_PS1_V1
 # define PM_MODEL_PARAMS_FROM_PSF pmModelParamsFromPSF_PS1_V1
@@ -70,15 +72,15 @@
 // Lax parameter limits
 static float paramsMinLax[] = { -1.0e3, 1.0e-2, -100, -100, 0.5, 0.5, -1.0, -1.0 };
-static float paramsMaxLax[] = { 1.0e5, 1.0e8, 1.0e4, 1.0e4, 100, 100, 1.0, 20.0 };
+static float paramsMaxLax[] = { 1.0e5, 1.0e9, 1.0e5, 1.0e5, 100, 100, 1.0, 20.0 };
 
 // Moderate parameter limits
 // Tolerate a small divot (k < 0)
 static float paramsMinModerate[] = { -1.0e3, 1.0e-2, -100, -100, 0.5, 0.5, -1.0, -0.05 };
-static float paramsMaxModerate[] = { 1.0e5, 1.0e8, 1.0e4, 1.0e4, 100, 100, 1.0, 20.0 };
+static float paramsMaxModerate[] = { 1.0e5, 1.0e9, 1.0e5, 1.0e5, 100, 100, 1.0, 20.0 };
 
 // Strict parameter limits
 // k = PAR_7 < 0 is very undesirable (big divot in the middle)
 static float paramsMinStrict[] = { -1.0e3, 1.0e-2, -100, -100, 0.5, 0.5, -1.0, 0.0 };
-static float paramsMaxStrict[] = { 1.0e5, 1.0e8, 1.0e4, 1.0e4, 100, 100, 1.0, 20.0 };
+static float paramsMaxStrict[] = { 1.0e5, 1.0e9, 1.0e5, 1.0e5, 100, 100, 1.0, 20.0 };
 
 // Parameter limits to use
@@ -335,7 +337,54 @@
 }
 
+// I used the script in models/fwhm.sh to generate the trend of FWHM scaling vs the K value
+// FWHM = Scale * Sigma (not that PAR[PM_PAR_SXX] = sigma * sqrt(2)
+//  K : z_hm  : FWHM
+//  0 : 1.000 : 2.83
+//  1 : 0.597 : 2.19
+//  2 : 0.396 : 1.78
+//  3 : 0.291 : 1.53
+//  4 : 0.232 : 1.36
+//  5 : 0.198 : 1.26
+//  6 : 0.169 : 1.16
+//  7 : 0.142 : 1.07
+//  8 : 0.124 : 0.99
+//  9 : 0.118 : 0.97
+// 10 : 0.106 : 0.92
+// 11 : 0.092 : 0.86
+// 12 : 0.091 : 0.85
+// 13 : 0.080 : 0.80
+// 14 : 0.078 : 0.79
+// 15 : 0.073 : 0.76
+// 16 : 0.063 : 0.71
+// 17 : 0.068 : 0.74
+// 18 : 0.056 : 0.67
+// 19 : 0.058 : 0.68
+
+// static float PS1_V1_Core[] = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0};
+static float PS1_V1_Scale[] = {2.83, 2.19, 1.78, 1.53, 1.36, 1.26, 1.16, 1.07, 0.99, 0.97, 0.92, 0.86, 0.85, 0.80, 0.79, 0.76, 0.71, 0.74, 0.67, 0.68};
+
+psF64 PM_MODEL_SET_FWHM (const psVector *params, psF64 sigma) {
+
+    psF32 *PAR = params->data.F32;
+
+    float core = PAR[PM_PAR_7];
+
+    if (!isfinite(core)) return (2.0*M_SQRT2*sigma);
+
+    // if PS1_V1_Core is defined as a set of integer steps, so we can simplify:
+    int binCore = MAX(0, MIN (19, (int)core));
+
+    float scale = NAN;
+    if (binCore == 0) {
+	scale = (core - binCore + 0) * (PS1_V1_Scale[binCore + 1] - PS1_V1_Scale[binCore + 0]) + PS1_V1_Scale[binCore + 0];
+    } else {
+	scale = (core - binCore - 1) * (PS1_V1_Scale[binCore + 0] - PS1_V1_Scale[binCore - 1]) + PS1_V1_Scale[binCore - 1];
+    }
+
+    return (scale * sigma);
+}
+
 bool PM_MODEL_FROM_PSF (pmModel *modelPSF, pmModel *modelFLT, const pmPSF *psf)
 {
-
     psF32 *out = modelPSF->params->data.F32;
     psF32 *in  = modelFLT->params->data.F32;
@@ -356,5 +405,5 @@
     // the 2D PSF model fits polarization terms (E0,E1,E2)
     // convert to shape terms (SXX,SYY,SXY)
-    bool useReff = pmModelUseReff (modelPSF->type);
+    bool useReff = modelPSF->class->useReff;
     if (!pmPSF_FitToModel (out, 0.1, useReff)) {
         psTrace("psModules.objects", 5, "Failed to fit object at (r,c) = (%.1f,%.1f)", in[PM_PAR_YPOS], in[PM_PAR_XPOS]);
@@ -410,5 +459,5 @@
     // convert to shape terms (SXX,SYY,SXY)
     // XXX user-defined value for limit?
-    bool useReff = pmModelUseReff (model->type);
+    bool useReff = model->class->useReff;
     if (!pmPSF_FitToModel (PAR, 0.1, useReff)) {
         psTrace ("psModules.objects", 3, "Failed to fit object at (r,c) = (%.1f,%.1f)", Xo, Yo);
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_PS1_V1.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_PS1_V1.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_PS1_V1.h	(revision 37066)
@@ -8,4 +8,5 @@
 psF64 pmModelFlux_PS1_V1(const psVector *params);
 psF64 pmModelRadius_PS1_V1(const psVector *params, psF64 flux);
+psF64 pmModelSetFWHM_PS1_V1(const psVector *params, psF64 flux);
 bool pmModelFromPSF_PS1_V1(pmModel *modelPSF, pmModel *modelFLT, const pmPSF *psf);
 bool  pmModelParamsFromPSF_PS1_V1(pmModel *model, const pmPSF *psf, float Xo, float Yo, float Io);
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_QGAUSS.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_QGAUSS.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_QGAUSS.c	(revision 37066)
@@ -35,11 +35,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -56,4 +57,5 @@
 # define PM_MODEL_LIMITS          pmModelLimits_QGAUSS
 # define PM_MODEL_RADIUS          pmModelRadius_QGAUSS
+# define PM_MODEL_SET_FWHM        pmModelSetFWHM_QGAUSS
 # define PM_MODEL_FROM_PSF        pmModelFromPSF_QGAUSS
 # define PM_MODEL_PARAMS_FROM_PSF pmModelParamsFromPSF_QGAUSS
@@ -70,15 +72,15 @@
 // Lax parameter limits
 static float paramsMinLax[] = { -1.0e3, 1.0e-2, -100, -100, 0.5, 0.5, -1.0, -1.0 };
-static float paramsMaxLax[] = { 1.0e5, 1.0e8, 1.0e4, 1.0e4, 100, 100, 1.0, 20.0 };
+static float paramsMaxLax[] = { 1.0e5, 1.0e9, 1.0e5, 1.0e5, 100, 100, 1.0, 20.0 };
 
 // Moderate parameter limits
 // Tolerate a small divot (k < 0)
 static float paramsMinModerate[] = { -1.0e3, 1.0e-2, -100, -100, 0.5, 0.5, -1.0, -0.05 };
-static float paramsMaxModerate[] = { 1.0e5, 1.0e8, 1.0e4, 1.0e4, 100, 100, 1.0, 20.0 };
+static float paramsMaxModerate[] = { 1.0e5, 1.0e9, 1.0e5, 1.0e5, 100, 100, 1.0, 20.0 };
 
 // Strict parameter limits
 // k = PAR_7 < 0 is very undesirable (big divot in the middle)
 static float paramsMinStrict[] = { -1.0e3, 1.0e-2, -100, -100, 0.5, 0.5, -1.0, 0.0 };
-static float paramsMaxStrict[] = { 1.0e5, 1.0e8, 1.0e4, 1.0e4, 100, 100, 1.0, 20.0 };
+static float paramsMaxStrict[] = { 1.0e5, 1.0e9, 1.0e5, 1.0e5, 100, 100, 1.0, 20.0 };
 
 // Parameter limits to use
@@ -336,4 +338,52 @@
 }
 
+// I used the script in models/fwhm.sh to generate the trend of FWHM scaling vs the K value
+// FWHM = Scale * Sigma (not that PAR[PM_PAR_SXX] = sigma * sqrt(2)
+//  K : z_hm  : FWHM
+//  0 : 1.000 : 2.83
+//  1 : 0.648 : 2.28
+//  2 : 0.430 : 1.85
+//  3 : 0.310 : 1.58
+//  4 : 0.244 : 1.40
+//  5 : 0.200 : 1.26
+//  6 : 0.165 : 1.15
+//  7 : 0.149 : 1.09
+//  8 : 0.125 : 1.00
+//  9 : 0.116 : 0.96
+// 10 : 0.101 : 0.90
+// 11 : 0.095 : 0.87
+// 12 : 0.083 : 0.82
+// 13 : 0.082 : 0.81
+// 14 : 0.080 : 0.80
+// 15 : 0.074 : 0.77
+// 16 : 0.064 : 0.71
+// 17 : 0.068 : 0.74
+// 18 : 0.057 : 0.67
+// 19 : 0.058 : 0.68
+
+// static float QGAUSS_Core[] = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0};
+static float QGAUSS_Scale[] = {2.83, 2.28, 1.85, 1.58, 1.40, 1.26, 1.15, 1.09, 1.00, 0.96, 0.90, 0.87, 0.82, 0.81, 0.80, 0.77, 0.71, 0.74, 0.67, 0.68};
+
+psF64 PM_MODEL_SET_FWHM (const psVector *params, psF64 sigma) {
+
+    psF32 *PAR = params->data.F32;
+
+    float core = PAR[PM_PAR_7];
+
+    if (!isfinite(core)) return (2.0*M_SQRT2*sigma);
+
+    // QGAUSS_Core is defined as a set of integer steps, so we can simplify:
+    int binCore = MAX(0, MIN (19, (int)core));
+
+    float scale = NAN;
+    if (binCore == 0) {
+	scale = (core - binCore + 0) * (QGAUSS_Scale[binCore + 1] - QGAUSS_Scale[binCore + 0]) + QGAUSS_Scale[binCore + 0];
+    } else {
+	scale = (core - binCore - 1) * (QGAUSS_Scale[binCore + 0] - QGAUSS_Scale[binCore - 1]) + QGAUSS_Scale[binCore - 1];
+    }
+
+    return (scale * sigma);
+}
+
 bool PM_MODEL_FROM_PSF (pmModel *modelPSF, pmModel *modelFLT, const pmPSF *psf)
 {
@@ -357,5 +407,5 @@
     // the 2D PSF model fits polarization terms (E0,E1,E2)
     // convert to shape terms (SXX,SYY,SXY)
-    bool useReff = pmModelUseReff (modelPSF->type);
+    bool useReff = modelPSF->class->useReff;
     if (!pmPSF_FitToModel (out, 0.1, useReff)) {
         psTrace("psModules.objects", 5, "Failed to fit object at (r,c) = (%.1f,%.1f)", in[PM_PAR_YPOS], in[PM_PAR_XPOS]);
@@ -415,5 +465,5 @@
     // the 2D PSF model fits polarization terms (E0,E1,E2)
     // convert to shape terms (SXX,SYY,SXY)
-    bool useReff = pmModelUseReff (model->type);
+    bool useReff = model->class->useReff;
     if (!pmPSF_FitToModel (PAR, 0.1, useReff)) {
         psTrace ("psModules.objects", 3, "Failed to fit object at (r,c) = (%.1f,%.1f)", Xo, Yo);
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_QGAUSS.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_QGAUSS.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_QGAUSS.h	(revision 37066)
@@ -8,4 +8,5 @@
 psF64 pmModelFlux_QGAUSS(const psVector *params);
 psF64 pmModelRadius_QGAUSS(const psVector *params, psF64 flux);
+psF64 pmModelSetFWHM_QGAUSS(const psVector *params, psF64 flux);
 bool pmModelFromPSF_QGAUSS(pmModel *modelPSF, pmModel *modelFLT, const pmPSF *psf);
 bool  pmModelParamsFromPSF_QGAUSS(pmModel *model, const pmPSF *psf, float Xo, float Yo, float Io);
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_RGAUSS.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_RGAUSS.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_RGAUSS.c	(revision 37066)
@@ -34,11 +34,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -55,4 +56,5 @@
 # define PM_MODEL_LIMITS          pmModelLimits_RGAUSS
 # define PM_MODEL_RADIUS          pmModelRadius_RGAUSS
+# define PM_MODEL_SET_FWHM        pmModelSetFWHM_RGAUSS
 # define PM_MODEL_FROM_PSF        pmModelFromPSF_RGAUSS
 # define PM_MODEL_PARAMS_FROM_PSF pmModelParamsFromPSF_RGAUSS
@@ -66,5 +68,5 @@
 // Lax parameter limits
 static float paramsMinLax[] = { -1.0e3, 1.0e-2, -100, -100, 0.5, 0.5, -1.0, 1.25 };
-static float paramsMaxLax[] = { 1.0e5, 1.0e8, 1.0e4, 1.0e4, 100, 100, 1.0, 4.0 };
+static float paramsMaxLax[] = { 1.0e5, 1.0e9, 1.0e5, 1.0e5, 100, 100, 1.0, 4.0 };
 
 // Moderate parameter limits
@@ -329,4 +331,8 @@
 }
 
+psF64 PM_MODEL_SET_FWHM (const psVector *params, psF64 sigma) {
+  return (NAN);
+}
+
 bool PM_MODEL_FROM_PSF (pmModel *modelPSF, pmModel *modelFLT, const pmPSF *psf)
 {
@@ -350,5 +356,5 @@
     // the 2D PSF model fits polarization terms (E0,E1,E2)
     // convert to shape terms (SXX,SYY,SXY)
-    bool useReff = pmModelUseReff (modelPSF->type);
+    bool useReff = modelPSF->class->useReff;
     if (!pmPSF_FitToModel (out, 0.1, useReff)) {
         psTrace("psModules.objects", 5, "Failed to fit object at (r,c) = (%.1f,%.1f)", in[PM_PAR_YPOS], in[PM_PAR_XPOS]);
@@ -403,5 +409,5 @@
     // the 2D PSF model fits polarization terms (E0,E1,E2)
     // convert to shape terms (SXX,SYY,SXY)
-    bool useReff = pmModelUseReff (model->type);
+    bool useReff = model->class->useReff;
     if (!pmPSF_FitToModel (PAR, 0.1, useReff)) {
         psTrace ("psModules.objects", 3, "Failed to fit object at (r,c) = (%.1f,%.1f)", Xo, Yo);
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_RGAUSS.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_RGAUSS.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_RGAUSS.h	(revision 37066)
@@ -8,4 +8,5 @@
 psF64 pmModelFlux_RGAUSS(const psVector *params);
 psF64 pmModelRadius_RGAUSS(const psVector *params, psF64 flux);
+psF64 pmModelSetFWHM_RGAUSS(const psVector *params, psF64 flux);
 bool pmModelFromPSF_RGAUSS(pmModel *modelPSF, pmModel *modelFLT, const pmPSF *psf);
 bool  pmModelParamsFromPSF_RGAUSS(pmModel *model, const pmPSF *psf, float Xo, float Yo, float Io);
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_SERSIC.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_SERSIC.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_SERSIC.c	(revision 37066)
@@ -20,5 +20,5 @@
    * note that a Sersic model is usually defined in terms of R_e, the half-light radius.  This
      construction does not include a factor of 2 in the X^2 term, etc, like for a Gaussian.
-     Conversion from SXX, SYY, SXY to R_major, R_minor, theta can be done by using setting:
+     Conversion from SXX, SYY, SXY to R_major, R_minor, theta can be done by using:
      shape.sx = SXX / sqrt(2), shape.sy = SYY / sqrt(2), shape.sxy = SXY, then calling
      psEllipseShapeToAxes, and multiplying the values of axes.major, axes.minor by sqrt(2)
@@ -43,11 +43,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -55,4 +56,5 @@
 #include "pmPSFtry.h"
 #include "pmDetections.h"
+#include "pmModel_CentralPixel.h"
 
 #include "pmModel_SERSIC.h"
@@ -64,4 +66,5 @@
 # define PM_MODEL_LIMITS          pmModelLimits_SERSIC
 # define PM_MODEL_RADIUS          pmModelRadius_SERSIC
+# define PM_MODEL_SET_FWHM        pmModelSetFWHM_SERSIC
 # define PM_MODEL_FROM_PSF        pmModelFromPSF_SERSIC
 # define PM_MODEL_PARAMS_FROM_PSF pmModelParamsFromPSF_SERSIC
@@ -74,6 +77,6 @@
 
 // Lax parameter limits
-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 };
+static float paramsMinLax[] = { -1.0e3, 1.0e-2, -100, -100, 0.001, 0.001, -1.0, 0.0625 };
+static float paramsMaxLax[] = { 1.0e5, 1.0e9, 1.0e5, 1.0e5, 100, 100, 1.0, 1.0 };
 
 // Moderate parameter limits
@@ -88,9 +91,9 @@
 static float *paramsMinUse = paramsMinLax;
 static float *paramsMaxUse = paramsMaxLax;
-static float betaUse[] = { 1000, 3e6, 5, 5, 1.0, 1.0, 0.5, 2.0 };
+static float betaUse[] = { 2, 3e6, 5, 5, 10.0, 10.0, 0.5, 1.0};
 
 static bool limitsApply = true;         // Apply limits?
 
-# include "pmModel_SERSIC.CP.h"
+// # include "pmModel_SERSIC.CP.h"
 
 psF32 PM_MODEL_FUNC (psVector *deriv,
@@ -111,91 +114,33 @@
     psAssert (z >= 0, "do not allow negative z values in model");
 
-    float index = 0.5 / PAR[PM_PAR_7];
-    float par7 = PAR[PM_PAR_7];
-    float bn = 1.9992*index - 0.3271;
-    float Io = exp(bn);
-
-    psF32 f2 = bn*pow(z,par7);
-    psF32 f1 = Io*exp(-f2);
+    float Sindex = 0.5 / PAR[PM_PAR_7];
+    float kappa = pmSersicKappa (Sindex);
+
+    float q = kappa*pow(z,PAR[PM_PAR_7]);
+    psF32 f0 = exp(-q);
+
+    assert (isfinite(q));
 
     psF32 radius = hypot(X, Y);
-    if (radius < 1.0) {
-
-	// ** use bilinear interpolation to the given location from the 4 surrounding pixels centered on the object center
-
-	// first, use Rmajor and index to find the central pixel flux (fraction of total flux)
-	psEllipseAxes axes;
-	pmModelParamsToAxes (&axes, PAR[PM_PAR_SXX], PAR[PM_PAR_SXY], PAR[PM_PAR_SYY], true);
-
-	// get the central pixel flux from the lookup table
-	float xPix = (axes.major - centralPixelXo) / centralPixeldX;
-	xPix = PS_MIN (PS_MAX(xPix, 0), centralPixelNX - 1);
-	float yPix = (index - centralPixelYo) / centralPixeldY;
-	yPix = PS_MIN (PS_MAX(yPix, 0), centralPixelNY - 1);
-
-	// the integral of a Sersic has an analytical form as follows:
-	float logGamma = lgamma(2.0*index);
-	float bnFactor = pow(bn, 2.0*index);
-	float norm = 2.0 * M_PI * PS_SQR(axes.major) * index * exp(bn) * exp(logGamma) / bnFactor;
-
-	// XXX interpolate to get the value
-	// XXX for the moment, just integerize
-	// XXX I need to multiply by the integrated flux to get the flux in the central pixel
-	float Vcenter = centralPixel[(int)yPix][(int)xPix] * norm;
-	
-	float px1 = 1.0 / PAR[PM_PAR_SXX];
-	float py1 = 1.0 / PAR[PM_PAR_SYY];
-	float z10 = PS_SQR(px1);
-	float z01 = PS_SQR(py1);
-
-	// which pixels do we need for this interpolation?
-	// (I do not keep state information, so I don't know anything about other evaluations of nearby pixels...)
-	if ((X >= 0) && (Y >= 0)) {
-	    float z11 = z10 + z01 + PAR[PM_PAR_SXY]; // X * Y positive
-	    float V00 = Vcenter;
-	    float V10 = Io*exp(-bn*pow(z10,par7));
-	    float V01 = Io*exp(-bn*pow(z01,par7));
-	    float V11 = Io*exp(-bn*pow(z11,par7));
-	    f1 = interpolatePixels(V00, V10, V01, V11, X, Y);
-	}
-	if ((X < 0) && (Y >= 0)) {
-	    float z11 = z10 + z01 - PAR[PM_PAR_SXY]; // X * Y negative
-	    float V00 = Io*exp(-bn*pow(z10,par7));
-	    float V10 = Vcenter;
-	    float V01 = Io*exp(-bn*pow(z11,par7));
-	    float V11 = Io*exp(-bn*pow(z01,par7));
-	    f1 = interpolatePixels(V00, V10, V01, V11, (1.0 + X), Y);
-	}
-	if ((X >= 0) && (Y < 0)) {
-	    float z11 = z10 + z01 - PAR[PM_PAR_SXY]; // X * Y negative
-	    float V00 = Io*exp(-bn*pow(z01,par7));
-	    float V10 = Io*exp(-bn*pow(z11,par7));
-	    float V01 = Vcenter;
-	    float V11 = Io*exp(-bn*pow(z10,par7));
-	    f1 = interpolatePixels(V00, V10, V01, V11, X, (1.0 + Y));
-	}
-	if ((X < 0) && (Y < 0)) {
-	    float z11 = z10 + z01 + PAR[PM_PAR_SXY]; // X * Y positive
-	    float V00 = Io*exp(-bn*pow(z11,par7));
-	    float V10 = Io*exp(-bn*pow(z10,par7));
-	    float V01 = Io*exp(-bn*pow(z01,par7));
-	    float V11 = Vcenter;
-	    f1 = interpolatePixels(V00, V10, V01, V11, (1.0 + X), (1.0 + Y));
-	}
-    }   
-
-    psF32 z0 = PAR[PM_PAR_I0]*f1;
-    psF32 f0 = PAR[PM_PAR_SKY] + z0;
-
-    if (!isfinite(z0)) {
-        fprintf(stderr, "z0 is not finite for %f %f %f %f %f.  Parameters: \n", X, Y, radius, z, f1);
+    if (radius <= 1.5) {
+	// Nsub ~ 10*index^2 + 1
+	psEllipseAxes axes = pmPSF_ModelToAxes(PAR, true); // SERSIC model uses Reff
+	int Nsub = 2 * ((int)(6.0*Sindex / axes.minor)) + 1;
+	Nsub = PS_MIN (Nsub, 121);
+	Nsub = PS_MAX (Nsub, 11);
+	f0 = pmModelCP_SersicSubpix (X, Y, PAR[PM_PAR_SXX], PAR[PM_PAR_SXY], PAR[PM_PAR_SYY], Sindex, Nsub);
+    }
+    if (!isfinite(f0)) {
+        fprintf(stderr, "f0 is not finite for %f %f %f %f %f.  Parameters: \n", X, Y, radius, z, q);
         fprintf(stderr, "%f %f %f %f %f %f %f %f\n", PAR[0], PAR[1], PAR[2], PAR[3], PAR[4],
             PAR[5], PAR[6], PAR[7]);
     }
-
-    assert (isfinite(f2));
+    assert (isfinite(f0));
+
+    psF32 f1 = PAR[PM_PAR_I0]*f0;
+    psF32 f = PAR[PM_PAR_SKY] + f1;
+
     assert (isfinite(f1));
-    assert (isfinite(z0));
-    assert (isfinite(f0));
+    assert (isfinite(f));
 
     if (deriv != NULL) {
@@ -203,22 +148,28 @@
 
         dPAR[PM_PAR_SKY]  = +1.0;
-        dPAR[PM_PAR_I0]   = +f1;
-
-        // gradient is infinite for z = 0; saturate at z = 0.01
-        psF32 z1 = (z < 0.01) ? z0*bn*par7*pow(0.01,par7 - 1.0) : z0*bn*par7*pow(z,par7 - 1.0);
-
-        dPAR[PM_PAR_7]    = (z < 0.01) ? -z0*pow(0.01,par7)*log(0.01) : -z0*f2*log(z);
-	dPAR[PM_PAR_7]   *= 3.0;
-
-        assert (isfinite(z1));
+        dPAR[PM_PAR_I0]   = +f0;
+
+	if (z > 0.01) {
+	  float z1 = f1*kappa*PAR[PM_PAR_7]*pow(z,PAR[PM_PAR_7]-1.0);
+	  dPAR[PM_PAR_XPOS] = +1.0*z1*(2.0*px + Y*PAR[PM_PAR_SXY]);
+	  dPAR[PM_PAR_YPOS] = +1.0*z1*(2.0*py + 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;
+	  dPAR[PM_PAR_7]    = -1.0*f1*q*log(z);
+	} else {
+	  // gradient -> 0 for z -> 0, but has undef form
+	  float z1 = f1*kappa*PAR[PM_PAR_7]*pow(z,PAR[PM_PAR_7]);
+	  dPAR[PM_PAR_XPOS] = +1.0*z1*(2.0/PAR[PM_PAR_SXX] + PAR[PM_PAR_SXY]);
+	  dPAR[PM_PAR_YPOS] = +1.0*z1*(2.0/PAR[PM_PAR_SYY] + PAR[PM_PAR_SXY]);
+	  dPAR[PM_PAR_SXX]  = +2.0*z1*px/PAR[PM_PAR_SXX]/PAR[PM_PAR_SXX];
+	  dPAR[PM_PAR_SYY]  = +2.0*z1*py/PAR[PM_PAR_SYY]/PAR[PM_PAR_SYY];
+	  dPAR[PM_PAR_SXY]  = -1.0*z1;
+	  // dPAR[PM_PAR_7]    = -1.0*f1*q*log(z + 0.0001);
+	  dPAR[PM_PAR_7]    = -1.0*f1*q*log(z + 0.0001); // factor of 16 to reduce the gain
+	}
         assert (isfinite(dPAR[PM_PAR_7]));
-
-        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]; // 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;
-    }
-    return (f0);
+    }
+    return (f);
 }
 
@@ -370,17 +321,11 @@
     psEllipseAxes axes;
     pmModelParamsToAxes (&axes, PAR[PM_PAR_SXX], PAR[PM_PAR_SXY], PAR[PM_PAR_SYY], true);
-    float AspectRatio = axes.minor / axes.major;
-
-    float index = 0.5 / PAR[PM_PAR_7];
-    float bn = 1.9992*index - 0.3271;
-
-    // the integral of a Sersic has an analytical form as follows:
-    float logGamma = lgamma(2.0*index);
-    float bnFactor = pow(bn, 2.0*index);
-    float norm = 2.0 * M_PI * PS_SQR(axes.major) * index * exp(bn) * exp(logGamma) / bnFactor;
-    
-    psF64 Flux = PAR[PM_PAR_I0] * norm * AspectRatio;
-
-    return(Flux);
+
+    float Sindex = 0.5 / PAR[PM_PAR_7];
+    float norm = pmSersicNorm (Sindex);
+
+    float flux = PAR[PM_PAR_I0] * 2.0 * M_PI * axes.major * axes.minor * norm;
+
+    return(flux);
 }
 
@@ -401,11 +346,18 @@
     pmModelParamsToAxes (&axes, PAR[PM_PAR_SXX], PAR[PM_PAR_SXY], PAR[PM_PAR_SYY], true);
 
+    float Sindex = 0.5 / PAR[PM_PAR_7];
+    float kappa = pmSersicKappa (Sindex);
+
     // 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]);
+    psF64 zn = log(PAR[PM_PAR_I0] / flux) / kappa;
+    psF64 radius = axes.major * pow(zn, Sindex);
 
     psAssert (isfinite(radius), "fix this code: radius should not be nan for Io = %f, flux = %f, major = %f (%f, %f, %f), par 7 = %f", 
 	      PAR[PM_PAR_I0], flux, axes.major, PAR[PM_PAR_SXX], PAR[PM_PAR_SXY], PAR[PM_PAR_SYY], PAR[PM_PAR_7]);
     return (radius);
+}
+
+psF64 PM_MODEL_SET_FWHM (const psVector *params, psF64 sigma) {
+  return (NAN);
 }
 
@@ -431,5 +383,5 @@
     // the 2D PSF model fits polarization terms (E0,E1,E2)
     // convert to shape terms (SXX,SYY,SXY)
-    bool useReff = pmModelUseReff (modelPSF->type);
+    bool useReff = modelPSF->class->useReff;
     if (!pmPSF_FitToModel (out, 0.1, useReff)) {
         psTrace("psModules.objects", 5, "Failed to fit object at (r,c) = (%.1f,%.1f)", in[PM_PAR_YPOS], in[PM_PAR_XPOS]);
@@ -485,5 +437,5 @@
     // convert to shape terms (SXX,SYY,SXY)
     // XXX user-defined value for limit?
-    bool useReff = pmModelUseReff (model->type);
+    bool useReff = model->class->useReff;
     if (!pmPSF_FitToModel (PAR, 0.1, useReff)) {
         psTrace ("psModules.objects", 3, "Failed to fit object at (r,c) = (%.1f,%.1f)", Xo, Yo);
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_SERSIC.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_SERSIC.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_SERSIC.h	(revision 37066)
@@ -8,4 +8,5 @@
 psF64 pmModelFlux_SERSIC(const psVector *params);
 psF64 pmModelRadius_SERSIC(const psVector *params, psF64 flux);
+psF64 pmModelSetFWHM_SERSIC(const psVector *params, psF64 flux);
 bool pmModelFromPSF_SERSIC(pmModel *modelPSF, pmModel *modelFLT, const pmPSF *psf);
 bool  pmModelParamsFromPSF_SERSIC(pmModel *model, const pmPSF *psf, float Xo, float Yo, float Io);
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_TRAIL.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_TRAIL.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_TRAIL.c	(revision 37066)
@@ -5,4 +5,5 @@
  * 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 #
+#include "pmModelClass.h"
  * define names are listed in pmModel.h:
 
@@ -33,11 +34,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -54,4 +56,5 @@
 # define PM_MODEL_LIMITS          pmModelLimits_TRAIL
 # define PM_MODEL_RADIUS          pmModelRadius_TRAIL
+# define PM_MODEL_SET_FWHM        pmModelSetFWHM_TRAIL
 # define PM_MODEL_FROM_PSF        pmModelFromPSF_TRAIL
 # define PM_MODEL_PARAMS_FROM_PSF pmModelParamsFromPSF_TRAIL
@@ -61,5 +64,5 @@
 // Lax parameter limits 
 static float paramsMinLax[] = { -1.0e3, 1.0e-2, -1.0e2, -1.0e2,   0.5, -3.3, -0.5 };
-static float paramsMaxLax[] = {  1.0e5, 1.0e+8, +1.0e4, +1.0e4, 150.0, +3.3 , 5.0 };
+static float paramsMaxLax[] = {  1.0e5, 1.00+9, +1.0e5, +1.0e5, 150.0, +3.3 , 5.0 };
 
 // Moderate parameter limits
@@ -351,5 +354,5 @@
 
     psF32 *psfPAR  = source->modelPSF->params->data.F32;
-    bool useReff = pmModelUseReff (source->modelPSF->type);
+    bool useReff = source->modelPSF->class->useReff;
 
     psEllipseAxes psfAxes;
@@ -412,4 +415,8 @@
     // PAR_LENGTH is the unconvolved length.  add a bit for safety
     return (0.5*PAR[PM_PAR_LENGTH] + 2);
+}
+
+psF64 PM_MODEL_SET_FWHM (const psVector *params, psF64 sigma) {
+  return (NAN);
 }
 
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_TRAIL.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_TRAIL.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/models/pmModel_TRAIL.h	(revision 37066)
@@ -8,4 +8,5 @@
 psF64 pmModelFlux_TRAIL(const psVector *params);
 psF64 pmModelRadius_TRAIL(const psVector *params, psF64 flux);
+psF64 pmModelSetFWHM_TRAIL(const psVector *params, psF64 flux);
 bool pmModelFromPSF_TRAIL(pmModel *modelPSF, pmModel *modelFLT, const pmPSF *psf);
 bool  pmModelParamsFromPSF_TRAIL(pmModel *model, const pmPSF *psf, float Xo, float Yo, float Io);
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmDetEff.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmDetEff.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmDetEff.c	(revision 37066)
@@ -17,11 +17,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmFootprintCullPeaks.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmFootprintCullPeaks.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmFootprintCullPeaks.c	(revision 37066)
@@ -25,10 +25,10 @@
 bool dumpfootprints (pmFootprint *fp, pmFootprintSpans *fpSp);
 
- /*
-  * Examine the peaks in a pmFootprint, and throw away the ones that are not sufficiently
-  * isolated.  More precisely, for each peak find the highest coll that you'd have to traverse
-  * to reach a still higher peak --- and if that coll's more (less?) than nsigma DN below your
-  * starting point, discard the peak.
-  */
+/*
+ * Examine the peaks in a pmFootprint, and throw away the ones that are not sufficiently
+ * isolated.  More precisely, for each peak find the highest coll that you'd have to traverse
+ * to reach a still higher peak --- and if that coll's more (less?) than nsigma DN below your
+ * starting point, discard the peak.
+ */
 
 # define IN_PEAK 1
@@ -48,5 +48,5 @@
 
     if (fp->peaks == NULL || fp->peaks->n < 2) { // nothing to do
-        return PS_ERR_NONE;
+	return PS_ERR_NONE;
     }
 
@@ -91,16 +91,16 @@
 
 	// max flux is above threshold for brightest peak
-      pmPeak *maxPeak = NULL;
-      for (int i = 0; i < fp->peaks->n; i++) {
-	pmPeak *testPeak = fp->peaks->data[i];
-	float this_peak = useSmoothedImage ? testPeak->smoothFlux : testPeak->rawFlux;
+	pmPeak *maxPeak = NULL;
+	for (int i = 0; i < fp->peaks->n; i++) {
+	    pmPeak *testPeak = fp->peaks->data[i];
+	    float this_peak = useSmoothedImage ? testPeak->smoothFlux : testPeak->rawFlux;
 	
-	if (isfinite(this_peak)) {
-	  maxPeak = fp->peaks->data[i];
-	  break;
-	}
-      }
-      psAssert(maxPeak,"maxPeak was not set in these peaks");
-      //      = fp->peaks->data[0];
+	    if (isfinite(this_peak)) {
+		maxPeak = fp->peaks->data[i];
+		break;
+	    }
+	}
+	psAssert(maxPeak,"maxPeak was not set in these peaks");
+	//      = fp->peaks->data[0];
 	float maxFlux = useSmoothedImage ? maxPeak->smoothFlux : maxPeak->rawFlux;
 
@@ -130,8 +130,8 @@
 	}
 #if (0)
-        if (threshbounds->data.F32[threshbounds->n-1] > maxFlux) {
-            psWarning ("upper limit: %f does not include max flux: %f",
-                    threshbounds->data.F32[threshbounds->n-1], maxFlux);
-        }
+	if (threshbounds->data.F32[threshbounds->n-1] > maxFlux) {
+	    psWarning ("upper limit: %f does not include max flux: %f",
+		       threshbounds->data.F32[threshbounds->n-1], maxFlux);
+	}
 #endif
 	psHistogram *threshist = psHistogramAllocGeneric(threshbounds);
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmGrowthCurve.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmGrowthCurve.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmGrowthCurve.c	(revision 37066)
@@ -30,11 +30,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmGrowthCurveGenerate.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmGrowthCurveGenerate.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmGrowthCurveGenerate.c	(revision 37066)
@@ -37,11 +37,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmModel.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmModel.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmModel.c	(revision 37066)
@@ -33,10 +33,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
-#include "pmModelClass.h"
 
 static void modelFree(pmModel *tmp)
 {
     psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
+    if (!tmp) return;
+
     psFree(tmp->params);
     psFree(tmp->dparams);
@@ -90,13 +92,15 @@
     }
 
-    tmp->modelFunc          = class->modelFunc;
-    tmp->modelFlux          = class->modelFlux;
-    tmp->modelRadius        = class->modelRadius;
-    tmp->modelLimits        = class->modelLimits;
-    tmp->modelGuess         = class->modelGuess;
-    tmp->modelFromPSF       = class->modelFromPSF;
-    tmp->modelParamsFromPSF = class->modelParamsFromPSF;
-    tmp->modelFitStatus     = class->modelFitStatus;
-    tmp->modelSetLimits     = class->modelSetLimits;
+    tmp->class = class;
+
+    // tmp->modelFunc          = class->modelFunc;
+    // tmp->modelFlux          = class->modelFlux;
+    // tmp->modelRadius        = class->modelRadius;
+    // tmp->modelLimits        = class->modelLimits;
+    // tmp->modelGuess         = class->modelGuess;
+    // tmp->modelFromPSF       = class->modelFromPSF;
+    // tmp->modelParamsFromPSF = class->modelParamsFromPSF;
+    // tmp->modelFitStatus     = class->modelFitStatus;
+    // tmp->modelSetLimits     = class->modelSetLimits;
 
     psTrace("psModules.objects", 10, "---- %s() end ----\n", __func__);
@@ -156,5 +160,5 @@
     psF32 tmpF;
 
-    tmpF = model->modelFunc (NULL, model->params, x);
+    tmpF = model->class->modelFunc (NULL, model->params, x);
     psFree(x);
     psTrace("psModules.objects", 10, "---- %s() end ----\n", __func__);
@@ -176,5 +180,5 @@
     psF32 tmpF;
 
-    tmpF = model->modelFunc (NULL, model->params, x);
+    tmpF = model->class->modelFunc (NULL, model->params, x);
     psFree(x);
     psTrace("psModules.objects", 10, "---- %s() end ----\n", __func__);
@@ -217,9 +221,11 @@
     // the options allow us to modify various aspects of the model
     if (mode & PM_MODEL_OP_NORM) {
+	// if we are including the sky, renormalizing should force use to normalized down the sky flux
+	params->data.F32[PM_PAR_SKY] /= params->data.F32[PM_PAR_I0];
         params->data.F32[PM_PAR_I0] = 1.0;
     }
     if (!(mode & PM_MODEL_OP_SKY)) {
         params->data.F32[PM_PAR_SKY] = 0.0;
-    }
+    } 
     if (mode & PM_MODEL_OP_CENTER) {
         params->data.F32[PM_PAR_XPOS] = image->col0 + 0.5*image->numCols;
@@ -281,5 +287,5 @@
             // add in the desired components for this coordinate
             if (mode & PM_MODEL_OP_FUNC) {
-                pixelValue += model->modelFunc (NULL, params, x);
+                pixelValue += model->class->modelFunc (NULL, params, x);
             }
 
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmModel.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmModel.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmModel.h	(revision 37066)
@@ -47,14 +47,17 @@
     bool isPCM;				///< is this model fitted with PSF-convolution?
 
+    pmModelClass *class;
+
     // functions for this model which depend on the model class
-    pmModelFunc          modelFunc;
-    pmModelFlux          modelFlux;
-    pmModelRadius        modelRadius;
-    pmModelLimits        modelLimits;
-    pmModelGuessFunc     modelGuess;
-    pmModelFromPSFFunc   modelFromPSF;
-    pmModelParamsFromPSF modelParamsFromPSF;
-    pmModelFitStatusFunc modelFitStatus;
-    pmModelSetLimitsFunc modelSetLimits;
+    
+    // pmModelFunc          modelFunc;
+    // pmModelFlux          modelFlux;
+    // pmModelRadius        modelRadius;
+    // pmModelLimits        modelLimits;
+    // pmModelGuessFunc     modelGuess;
+    // pmModelFromPSFFunc   modelFromPSF;
+    // pmModelParamsFromPSF modelParamsFromPSF;
+    // pmModelFitStatusFunc modelFitStatus;
+    // pmModelSetLimitsFunc modelSetLimits;
 };
 
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmModelClass.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmModelClass.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmModelClass.c	(revision 37066)
@@ -33,7 +33,7 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 
 #include "pmErrorCodes.h"
@@ -54,16 +54,17 @@
 
 static pmModelClass defaultModels[] = {
-    {"PS_MODEL_GAUSS",        7, (pmModelFunc)pmModelFunc_GAUSS,   (pmModelFlux)pmModelFlux_GAUSS,   (pmModelRadius)pmModelRadius_GAUSS,   (pmModelLimits)pmModelLimits_GAUSS,   (pmModelGuessFunc)pmModelGuess_GAUSS,  (pmModelFromPSFFunc)pmModelFromPSF_GAUSS,  (pmModelParamsFromPSF)pmModelParamsFromPSF_GAUSS,  (pmModelFitStatusFunc)pmModelFitStatus_GAUSS,  (pmModelSetLimitsFunc)pmModelSetLimits_GAUSS  },
-    {"PS_MODEL_PGAUSS",       7, (pmModelFunc)pmModelFunc_PGAUSS,  (pmModelFlux)pmModelFlux_PGAUSS,  (pmModelRadius)pmModelRadius_PGAUSS,  (pmModelLimits)pmModelLimits_PGAUSS,  (pmModelGuessFunc)pmModelGuess_PGAUSS, (pmModelFromPSFFunc)pmModelFromPSF_PGAUSS, (pmModelParamsFromPSF)pmModelParamsFromPSF_PGAUSS, (pmModelFitStatusFunc)pmModelFitStatus_PGAUSS, (pmModelSetLimitsFunc)pmModelSetLimits_PGAUSS },
-    {"PS_MODEL_QGAUSS",       8, (pmModelFunc)pmModelFunc_QGAUSS,  (pmModelFlux)pmModelFlux_QGAUSS,  (pmModelRadius)pmModelRadius_QGAUSS,  (pmModelLimits)pmModelLimits_QGAUSS,  (pmModelGuessFunc)pmModelGuess_QGAUSS, (pmModelFromPSFFunc)pmModelFromPSF_QGAUSS, (pmModelParamsFromPSF)pmModelParamsFromPSF_QGAUSS, (pmModelFitStatusFunc)pmModelFitStatus_QGAUSS, (pmModelSetLimitsFunc)pmModelSetLimits_QGAUSS },
-    {"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_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    },
-    {"PS_MODEL_TRAIL",        7, (pmModelFunc)pmModelFunc_TRAIL,   (pmModelFlux)pmModelFlux_TRAIL,   (pmModelRadius)pmModelRadius_TRAIL,   (pmModelLimits)pmModelLimits_TRAIL,   (pmModelGuessFunc)pmModelGuess_TRAIL,  (pmModelFromPSFFunc)pmModelFromPSF_TRAIL,  (pmModelParamsFromPSF)pmModelParamsFromPSF_TRAIL,  (pmModelFitStatusFunc)pmModelFitStatus_TRAIL,  (pmModelSetLimitsFunc)pmModelSetLimits_TRAIL  },
+    {"PS_MODEL_GAUSS",        7, 0, (pmModelFunc)pmModelFunc_GAUSS,   (pmModelFlux)pmModelFlux_GAUSS,   (pmModelRadius)pmModelRadius_GAUSS,   (pmModelSetFWHM)pmModelSetFWHM_GAUSS,   (pmModelLimits)pmModelLimits_GAUSS,   (pmModelGuessFunc)pmModelGuess_GAUSS,  (pmModelFromPSFFunc)pmModelFromPSF_GAUSS,  (pmModelParamsFromPSF)pmModelParamsFromPSF_GAUSS,  (pmModelFitStatusFunc)pmModelFitStatus_GAUSS,  (pmModelSetLimitsFunc)pmModelSetLimits_GAUSS  },
+    {"PS_MODEL_PGAUSS",       7, 0, (pmModelFunc)pmModelFunc_PGAUSS,  (pmModelFlux)pmModelFlux_PGAUSS,  (pmModelRadius)pmModelRadius_PGAUSS,  (pmModelSetFWHM)pmModelSetFWHM_PGAUSS,  (pmModelLimits)pmModelLimits_PGAUSS,  (pmModelGuessFunc)pmModelGuess_PGAUSS, (pmModelFromPSFFunc)pmModelFromPSF_PGAUSS, (pmModelParamsFromPSF)pmModelParamsFromPSF_PGAUSS, (pmModelFitStatusFunc)pmModelFitStatus_PGAUSS, (pmModelSetLimitsFunc)pmModelSetLimits_PGAUSS },
+    {"PS_MODEL_QGAUSS",       8, 0, (pmModelFunc)pmModelFunc_QGAUSS,  (pmModelFlux)pmModelFlux_QGAUSS,  (pmModelRadius)pmModelRadius_QGAUSS,  (pmModelSetFWHM)pmModelSetFWHM_QGAUSS,  (pmModelLimits)pmModelLimits_QGAUSS,  (pmModelGuessFunc)pmModelGuess_QGAUSS, (pmModelFromPSFFunc)pmModelFromPSF_QGAUSS, (pmModelParamsFromPSF)pmModelParamsFromPSF_QGAUSS, (pmModelFitStatusFunc)pmModelFitStatus_QGAUSS, (pmModelSetLimitsFunc)pmModelSetLimits_QGAUSS },
+    {"PS_MODEL_PS1_V1",       8, 0, (pmModelFunc)pmModelFunc_PS1_V1,  (pmModelFlux)pmModelFlux_PS1_V1,  (pmModelRadius)pmModelRadius_PS1_V1,  (pmModelSetFWHM)pmModelSetFWHM_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, 0, (pmModelFunc)pmModelFunc_RGAUSS,  (pmModelFlux)pmModelFlux_RGAUSS,  (pmModelRadius)pmModelRadius_RGAUSS,  (pmModelSetFWHM)pmModelSetFWHM_RGAUSS,  (pmModelLimits)pmModelLimits_RGAUSS,  (pmModelGuessFunc)pmModelGuess_RGAUSS, (pmModelFromPSFFunc)pmModelFromPSF_RGAUSS, (pmModelParamsFromPSF)pmModelParamsFromPSF_RGAUSS, (pmModelFitStatusFunc)pmModelFitStatus_RGAUSS, (pmModelSetLimitsFunc)pmModelSetLimits_RGAUSS },
+    {"PS_MODEL_SERSIC",       8, 1, (pmModelFunc)pmModelFunc_SERSIC,  (pmModelFlux)pmModelFlux_SERSIC,  (pmModelRadius)pmModelRadius_SERSIC,  (pmModelSetFWHM)pmModelSetFWHM_SERSIC,  (pmModelLimits)pmModelLimits_SERSIC,  (pmModelGuessFunc)pmModelGuess_SERSIC, (pmModelFromPSFFunc)pmModelFromPSF_SERSIC, (pmModelParamsFromPSF)pmModelParamsFromPSF_SERSIC, (pmModelFitStatusFunc)pmModelFitStatus_SERSIC, (pmModelSetLimitsFunc)pmModelSetLimits_SERSIC },
+    {"PS_MODEL_EXP",          7, 1, (pmModelFunc)pmModelFunc_EXP,     (pmModelFlux)pmModelFlux_EXP,     (pmModelRadius)pmModelRadius_EXP,     (pmModelSetFWHM)pmModelSetFWHM_EXP,     (pmModelLimits)pmModelLimits_EXP,     (pmModelGuessFunc)pmModelGuess_EXP,    (pmModelFromPSFFunc)pmModelFromPSF_EXP,    (pmModelParamsFromPSF)pmModelParamsFromPSF_EXP,    (pmModelFitStatusFunc)pmModelFitStatus_EXP,    (pmModelSetLimitsFunc)pmModelSetLimits_EXP    },
+    {"PS_MODEL_DEV",          7, 1, (pmModelFunc)pmModelFunc_DEV,     (pmModelFlux)pmModelFlux_DEV,     (pmModelRadius)pmModelRadius_DEV,     (pmModelSetFWHM)pmModelSetFWHM_DEV,     (pmModelLimits)pmModelLimits_DEV,     (pmModelGuessFunc)pmModelGuess_DEV,    (pmModelFromPSFFunc)pmModelFromPSF_DEV,    (pmModelParamsFromPSF)pmModelParamsFromPSF_DEV,    (pmModelFitStatusFunc)pmModelFitStatus_DEV,    (pmModelSetLimitsFunc)pmModelSetLimits_DEV    },
+    {"PS_MODEL_TRAIL",        7, 0, (pmModelFunc)pmModelFunc_TRAIL,   (pmModelFlux)pmModelFlux_TRAIL,   (pmModelRadius)pmModelRadius_TRAIL,   (pmModelSetFWHM)pmModelSetFWHM_TRAIL,   (pmModelLimits)pmModelLimits_TRAIL,   (pmModelGuessFunc)pmModelGuess_TRAIL,  (pmModelFromPSFFunc)pmModelFromPSF_TRAIL,  (pmModelParamsFromPSF)pmModelParamsFromPSF_TRAIL,  (pmModelFitStatusFunc)pmModelFitStatus_TRAIL,  (pmModelSetLimitsFunc)pmModelSetLimits_TRAIL  },
 };
 
 static pmModelClass *models = NULL;
+static psVector *modelClassLookupTable = NULL;  // translation between model types in header and here
 static int Nmodels = 0;
 
@@ -135,4 +136,6 @@
     models = NULL;
     Nmodels = 0;
+    psFree(modelClassLookupTable);
+    modelClassLookupTable = NULL;
     return;
 }
@@ -193,2 +196,80 @@
 }
 
+
+bool pmModelClassWriteHeader(psMetadata *header)
+{
+    psMetadataAddS32(header, PS_LIST_TAIL, "MTNUM", PS_META_REPLACE, "number of model types", Nmodels);
+    for (int i = 0; i < Nmodels; i++) {
+        char modelNameKey[16];
+        char modelValKey[16];
+        sprintf(modelNameKey, "MTNAM%02d", i);
+        sprintf(modelValKey,  "MTVAL%02d", i);
+        psMetadataAddStr(header, PS_LIST_TAIL, modelNameKey, PS_META_REPLACE, "", models[i].name);
+        psMetadataAddS32(header, PS_LIST_TAIL, modelValKey, PS_META_REPLACE, "", i);
+    }
+
+    return true;
+}
+
+bool pmModelClassReadHeader(psMetadata *header) {
+    psFree(modelClassLookupTable);
+
+    bool status;
+    int numHeaderModels = psMetadataLookupS32(&status, header, "MTNUM");
+    if (!status) {
+        return false;
+    }
+
+    psVector *inputTypes = psVectorAlloc(numHeaderModels, PS_TYPE_S32);
+    psVector *localTypes = psVectorAlloc(numHeaderModels, PS_TYPE_S32);
+    int max_val = -1;
+    for (int i = 0; i < numHeaderModels; i++) {
+        char modelNameKey[16];
+        char modelValKey[16];
+        sprintf(modelNameKey, "MTNAM%02d", i);
+        sprintf(modelValKey,  "MTVAL%02d", i);
+        psString thisName = psMetadataLookupStr(&status, header, modelNameKey);
+        int thisVal = psMetadataLookupS32(&status, header, modelValKey);
+        if (thisVal > max_val) {
+            max_val = thisVal;
+        }
+        inputTypes->data.S32[i] = thisVal;
+        localTypes->data.S32[i] = pmModelClassGetType(thisName);
+    }
+    if (max_val < 0) {
+        psFree(inputTypes);
+        psFree(localTypes);
+        return false;
+    }
+
+    modelClassLookupTable = psVectorAlloc(max_val + 1, PS_TYPE_S32);
+    psVectorInit(modelClassLookupTable, -1);
+
+    for (int i = 0; i < numHeaderModels; i++) {
+        int thisVal = inputTypes->data.S32[i];
+        int localVal = localTypes->data.S32[i];
+        modelClassLookupTable->data.S32[thisVal] = localVal;
+    }
+    psFree(inputTypes);
+    psFree(localTypes);
+
+    return true;
+}
+
+pmModelType pmModelClassGetLocalType(pmModelType inputType) {
+    pmModelType localType = -1;
+
+    if (modelClassLookupTable) {
+        if (inputType >= 0 && inputType < modelClassLookupTable->n) {
+            localType = modelClassLookupTable->data.S32[inputType];
+        }
+    } else {
+        // no lookup table defined
+        // for backwards compatability if inputType refers to a defined model, return it
+        if (inputType >= 0 && pmModelClassGetName(inputType)) {
+            localType = inputType;
+        }
+    }
+
+    return localType;
+}
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmModelClass.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmModelClass.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmModelClass.h	(revision 37066)
@@ -35,7 +35,9 @@
     char *name;
     int nParams;
+    bool useReff;
     pmModelFunc          modelFunc;
     pmModelFlux          modelFlux;
     pmModelRadius        modelRadius;
+    pmModelSetFWHM       modelSetFWHM;
     pmModelLimits        modelLimits;
     pmModelGuessFunc     modelGuess;
@@ -76,4 +78,10 @@
 void pmModelClassSetLimits(pmModelLimitsType type);
 
+// write keywords to header definining the model type values used by this program
+bool pmModelClassWriteHeader(psMetadata *header);
+// create a lookup table for translating input model type values to local model type values
+bool pmModelClassReadHeader(psMetadata *header);
+// translate input model type value to local value
+pmModelType pmModelClassGetLocalType(pmModelType inputType);
 
 /// @}
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmModelFuncs.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmModelFuncs.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmModelFuncs.h	(revision 37066)
@@ -36,11 +36,16 @@
 
 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
+    PM_MODEL_STATUS_NONE           = 0x000, ///< model fit not yet attempted, no other info
+    PM_MODEL_STATUS_FITTED         = 0x001, ///< model fit completed
+    PM_MODEL_STATUS_NONCONVERGE    = 0x002, ///< model fit did not converge
+    PM_MODEL_STATUS_OFFIMAGE       = 0x004, ///< model fit drove out of range
+    PM_MODEL_STATUS_BADARGS        = 0x008, ///< model fit called with invalid args
+    PM_MODEL_STATUS_LIMITS         = 0x010, ///< model parameters hit limits
+    PM_MODEL_STATUS_WEAK_FIT       = 0x020, ///< model fit met loose tolerance, but not tight tolerance
+    PM_MODEL_STATUS_NAN_CHISQ      = 0x040, ///< model fit failed with a NAN chisq 
+    PM_MODEL_SERSIC_PCM_FAIL_GUESS = 0x080, ///< sersic model fit failed on the initial moments-based guess
+    PM_MODEL_SERSIC_PCM_FAIL_GRID  = 0x100, ///< sersic model fit failed on the grid search
+    PM_MODEL_PCM_FAIL_GUESS        = 0x200, ///< non-sersic model fit failed on the initial moments-based guess
+    PM_MODEL_BEST_FIT              = 0x400, ///< this model was the best fit and was subtracted
 } pmModelStatus;
 
@@ -105,4 +110,7 @@
 typedef psF64 (*pmModelRadius)(const psVector *params, double flux);
 
+// This function returns the FWHM given the supplied sigma (major or minor)
+typedef psF64 (*pmModelSetFWHM)(const psVector *params, double sigma);
+
 //  This function provides the model guess parameters based on the details of
 //  the given source.
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmModelUtils.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmModelUtils.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmModelUtils.c	(revision 37066)
@@ -32,11 +32,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -44,13 +45,4 @@
 
 #include "pmErrorCodes.h"
-
-// XX static bool useModelVar = false;
-// XX 
-// XX void pmModelSetModelVarOption (bool option) {
-// XX   useModelVar = option;
-// XX }
-// XX bool pmModelGetModelVarOption (void) {
-// XX   return useModelVar;
-// XX }
 
 /*****************************************************************************
@@ -67,5 +59,5 @@
 
     // set model parameters for this source based on PSF information
-    if (!modelEXT->modelFromPSF (modelPSF, modelEXT, psf)) {
+    if (!modelEXT->class->modelFromPSF (modelPSF, modelEXT, psf)) {
         psTrace ("psModules.objects", 3, "Failed to set model params from PSF");
         psFree(modelPSF);
@@ -88,5 +80,5 @@
 
     // set model parameters for this source based on PSF information
-    if (!modelPSF->modelParamsFromPSF (modelPSF, psf, Xo, Yo, Io)) {
+    if (!modelPSF->class->modelParamsFromPSF (modelPSF, psf, Xo, Yo, Io)) {
         psFree(modelPSF);
         return NULL;
@@ -108,5 +100,5 @@
 
     // determine the normalized flux
-    float normFlux = model->modelFlux (model->params);
+    float normFlux = model->class->modelFlux (model->params);
     assert (isfinite(normFlux));
     assert (normFlux > 0);
@@ -119,8 +111,8 @@
 
 bool pmModelUseReff (pmModelType type) {
-    bool useReff = false;
-    useReff |= (type == pmModelClassGetType ("PS_MODEL_SERSIC"));
-    useReff |= (type == pmModelClassGetType ("PS_MODEL_DEV"));
-    useReff |= (type == pmModelClassGetType ("PS_MODEL_EXP"));
+
+    pmModelClass *class = pmModelClassSelect (type);
+    psAssert (class, "undefined model class?");
+    bool useReff = class->useReff;
     return useReff;
 }
@@ -128,4 +120,8 @@
 // this function and the one below handle the two cases, where the model shape is uses R_eff or Sigma
 bool pmModelAxesToParams (float *Sxx, float *Sxy, float *Syy, psEllipseAxes axes, bool useReff)  {
+
+    // restrict axex to 0.5 here not below 
+    if (axes.minor < 0.2) axes.minor = 0.2;
+    if (axes.major < 0.2) axes.major = 0.2;
 
     psEllipseShape shape = psEllipseAxesToShape (axes);
@@ -137,10 +133,14 @@
     // set the shape parameters
     if (useReff) {
-	*Sxx  = PS_MAX(0.5, shape.sx);
-	*Syy  = PS_MAX(0.5, shape.sy);
+	// *Sxx  = PS_MAX(0.5, shape.sx);
+	// *Syy  = PS_MAX(0.5, shape.sy);
+	*Sxx  = shape.sx;
+	*Syy  = shape.sy;
 	*Sxy  = shape.sxy * 2.0;
     } else {
-	*Sxx  = PS_MAX(0.5, M_SQRT2*shape.sx);
-	*Syy  = PS_MAX(0.5, M_SQRT2*shape.sy);
+	// *Sxx  = PS_MAX(0.5, M_SQRT2*shape.sx);
+	// *Syy  = PS_MAX(0.5, M_SQRT2*shape.sy);
+	*Sxx  = M_SQRT2*shape.sx;
+	*Syy  = M_SQRT2*shape.sy;
 	*Sxy  = shape.sxy;
     }
@@ -193,4 +193,5 @@
     if (!isfinite(axes.minor)) return false;
     if (!isfinite(axes.theta)) return false;
+    if (axes.major == 0) return false;
 
     // Mxx, Mxy, Myy define the elliptical shape, but Mrf defines the width 
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmModel_CentralPixel.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmModel_CentralPixel.c	(revision 37066)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmModel_CentralPixel.c	(revision 37066)
@@ -0,0 +1,821 @@
+/* @file  pmModel_CentralPixel.c
+ * @brief Functions to manage the central pixel for sersic-like models
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.19 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2009-02-16 22:30:50 $
+ *
+ * Copyright 2013 Institute for Astronomy, University of Hawaii
+ */
+
+/******************************************************************************
+ * this file contains functions to determine the flux of the central pixel(s) for an
+ * exponential / devaucouleur / sersic style galaxy model.  The problem is that (a) these
+ * models are so centrally-peaked that it is necessary to determine the true mean flux in the
+ * central pixel by integration of fractional pixels (at least 0.02 pixels in the case of a
+ * DEV model) and (b) the process of integrating the central pixel is too slow to be used for
+ * real processing.
+
+ * we bypass this problem by defining a set of pre-calculated central pixel images, with
+ * subpixel resolution > 1 pixel (maybe 11 subpixels per real pixel).  These pre-calculated
+ * images are generated for a series of values for the following parameters: sersic index,
+ * effective radius, axial ratio.  We then select the closest image to our specific case, and
+ * integrate over the true sub-pixels relevant for our position and model.  We have thus turned
+ * problem from 2500 evaluations of the full sersic model to ~100 straight additions (possibly
+ * x 6 if we need to interpolate in each of the dimensions).  
+
+ * we need a number of support functions:
+
+ * pmModelCP_Load : load CP model data from the specified file. 
+ * pmModelCP_GetImage : choose an appropriate CP model image for a given set of parameters
+ * pmModelCP_GetValue : calculate the true CP value for the given image and parameters
+
+   *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <pslib.h>
+#include <pslib.h>
+
+#include "pmModel_CentralPixel.h"
+
+static void pmModelCP_Free(pmModelCP *cp) {
+    psFree (cp->flux);
+}
+
+pmModelCP *pmModelCP_Alloc(void)
+{
+    pmModelCP *tmp = (pmModelCP *) psAlloc(sizeof(pmModelCP));
+    psMemSetDeallocator(tmp, (psFreeFunc) pmModelCP_Free);
+
+    tmp->flux = NULL;
+    tmp->Rmajor = NAN;
+    tmp->Aratio = NAN;
+    tmp->Sindex = NAN;
+
+    return tmp;
+}
+
+static void pmModelCPset_Free(pmModelCPset *tmp) {
+
+    for (int i = 0; i < tmp->RmajorNitem; i++) {
+	for (int j = 0; j < tmp->AratioNitem; j++) {
+	    psFree (tmp->lookupCube[i][j]);
+	}
+	psFree (tmp->lookupCube[i]);
+    }
+    psFree (tmp->lookupCube);
+    psFree (tmp->images);
+}
+
+pmModelCPset *pmModelCPset_Alloc(void)
+{
+    pmModelCPset *tmp = (pmModelCPset *) psAlloc(sizeof(pmModelCPset));
+    psMemSetDeallocator(tmp, (psFreeFunc) pmModelCPset_Free);
+
+    tmp->RmajorMin = NAN;
+    tmp->RmajorMax = NAN;
+    tmp->RmajorDel = NAN;
+
+    tmp->AratioMin = NAN;
+    tmp->AratioMax = NAN;
+    tmp->AratioDel = NAN;
+
+    tmp->SindexMin = NAN;
+    tmp->SindexMax = NAN;
+    tmp->SindexDel = NAN;
+
+    tmp->RmajorNitem = 0;
+    tmp->AratioNitem = 0;
+    tmp->SindexNitem = 0;
+
+    tmp->lookupCube = NULL;
+    tmp->images = NULL;
+
+    return tmp;
+}
+
+// load the central-pixel maps as an array of pmModelCP 
+pmModelCPset *pmModelCP_Load (char *filename) {
+
+    bool status;
+
+    psFits *fits = psFitsOpen (filename, "r");
+    if (!fits) {
+	return false;
+    }
+    
+    // read the PHU -- it defines descriptive metadata
+    psMetadata *PHU = psFitsReadHeader (NULL, fits);
+    if (!PHU) {
+	psFitsClose (fits);
+	return false;
+    }
+
+    pmModelCPset *CPset = pmModelCPset_Alloc();
+
+    // NOTE : RMAJOR refers to the LOG_10 of the major axis
+    CPset->RmajorMin = psMetadataLookupF32 (&status, PHU, "RMAJ_MIN"); psAssert (status, "missing keyword RMAJ_MIN");
+    CPset->RmajorMax = psMetadataLookupF32 (&status, PHU, "RMAJ_MAX"); psAssert (status, "missing keyword RMAJ_MAX");
+    CPset->RmajorDel = psMetadataLookupF32 (&status, PHU, "RMAJ_DEL"); psAssert (status, "missing keyword RMAJ_DEL");
+    CPset->AratioMin = psMetadataLookupF32 (&status, PHU, "ARAT_MIN"); psAssert (status, "missing keyword ARAT_MIN");
+    CPset->AratioMax = psMetadataLookupF32 (&status, PHU, "ARAT_MAX"); psAssert (status, "missing keyword ARAT_MAX");
+    CPset->AratioDel = psMetadataLookupF32 (&status, PHU, "ARAT_DEL"); psAssert (status, "missing keyword ARAT_DEL");
+    CPset->SindexMin = psMetadataLookupF32 (&status, PHU, "SIDX_MIN"); psAssert (status, "missing keyword SIDX_MIN");
+    CPset->SindexMax = psMetadataLookupF32 (&status, PHU, "SIDX_MAX"); psAssert (status, "missing keyword SIDX_MAX");
+    CPset->SindexDel = psMetadataLookupF32 (&status, PHU, "SIDX_DEL"); psAssert (status, "missing keyword SIDX_DEL");
+
+    CPset->RmajorNitem = 1 + (int)(0.5 + (CPset->RmajorMax - CPset->RmajorMin) / CPset->RmajorDel);
+    CPset->AratioNitem = 1 + (int)(0.5 + (CPset->AratioMax - CPset->AratioMin) / CPset->AratioDel);
+    CPset->SindexNitem = 1 + (int)(0.5 + (CPset->SindexMax - CPset->SindexMin) / CPset->SindexDel);
+
+    // array entry = lookupCube[RmajorBin][AratioBin][SindexBin]
+
+    CPset->lookupCube = (int ***) psAlloc (sizeof(int **)*CPset->RmajorNitem);
+    for (int i = 0; i < CPset->RmajorNitem; i++) {
+	CPset->lookupCube[i] = (int **) psAlloc (sizeof(int *)*CPset->AratioNitem);
+	for (int j = 0; j < CPset->AratioNitem; j++) {
+	    CPset->lookupCube[i][j] = (int *) psAlloc (sizeof(int)*CPset->SindexNitem);
+	    for (int k = 0; k < CPset->SindexNitem; k++) {
+		CPset->lookupCube[i][j][k] = -1;
+	    }
+	}
+    }
+    
+    CPset->images = psArrayAllocEmpty (CPset->RmajorNitem*CPset->AratioNitem*CPset->SindexNitem);
+
+    // the CP file contains a set of 2D images; load them all 
+
+    psRegion fullImage = psRegionSet (0, 0, 0, 0);
+    while (true) {
+	bool status = psFitsMoveExtNum (fits, 1, true);
+	if (!status) break;
+
+	psMetadata *header = psFitsReadHeader (NULL, fits);
+	if (!header) {
+	    fprintf (stderr, "error reading header\n");
+	    return false;
+	}
+
+	pmModelCP *cp = pmModelCP_Alloc ();
+
+	cp->flux = psFitsReadImageBuffer (NULL, fits, fullImage, 0);
+	if (!cp->flux) {
+	    fprintf (stderr, "error reading image\n");
+	    return false;
+	}
+
+	cp->Rmajor = psMetadataLookupF32 (&status, header, "R_MAJOR");
+	cp->Aratio = psMetadataLookupF32 (&status, header, "A_RATIO");
+	cp->Sindex = psMetadataLookupF32 (&status, header, "S_INDEX");
+	
+	int RmajorBin = (int)((cp->Rmajor - CPset->RmajorMin) / CPset->RmajorDel); psAssert ((RmajorBin < CPset->RmajorNitem) && (RmajorBin >= 0), "bad bin");
+	int AratioBin = (int)((cp->Aratio - CPset->AratioMin) / CPset->AratioDel); psAssert ((AratioBin < CPset->AratioNitem) && (AratioBin >= 0), "bad bin");
+	int SindexBin = (int)((cp->Sindex - CPset->SindexMin) / CPset->SindexDel); psAssert ((SindexBin < CPset->SindexNitem) && (SindexBin >= 0), "bad bin");
+
+	CPset->lookupCube[RmajorBin][AratioBin][SindexBin] = CPset->images->n;
+
+	psArrayAdd (CPset->images, 121, cp);
+	psFree (cp);
+	psFree (header);
+    }
+
+    psFree (PHU);
+    psFitsClose (fits);
+
+    return CPset;
+}
+
+// choose the closest image to the given coords
+pmModelCP *pmModelCP_GetImage (pmModelCPset *CPset, float Rmajor, float Aratio, float Sindex) {
+
+    // the pmModelCP set is defined for a grid of Rmajor, Aratio, Sindex values
+
+    int RmajorBin = (int)((Rmajor - CPset->RmajorMin) / CPset->RmajorDel); psAssert ((RmajorBin < CPset->RmajorNitem) && (RmajorBin >= 0), "bad bin");
+    int AratioBin = (int)((Aratio - CPset->AratioMin) / CPset->AratioDel); psAssert ((AratioBin < CPset->AratioNitem) && (AratioBin >= 0), "bad bin");
+    int SindexBin = (int)((Sindex - CPset->SindexMin) / CPset->SindexDel); psAssert ((SindexBin < CPset->SindexNitem) && (SindexBin >= 0), "bad bin");
+    
+    int entry = CPset->lookupCube[RmajorBin][AratioBin][SindexBin];
+    
+    pmModelCP *cp = CPset->images->data[entry];
+
+    return (cp);
+}
+
+// XXX for test purposes only:
+# define TEST_IMAGE 0
+# if (TEST_IMAGE)
+static psImage *map = NULL;
+# endif
+
+float pmModelCP_GetFlux_RotSquare (pmModelCP *cp, float dx, float dy, float theta);
+
+float pmModelCP_GetFlux (pmModelCP *cp, float dx, float dy, float theta) {
+
+# if (TEST_IMAGE) 
+    map = psImageCopy (map, cp->flux, PS_TYPE_S32);
+    psImageInit (map, 0.0);
+# endif
+
+    // float flux = pmModelCP_GetFlux_Bresen (cp, dx, dy, theta);
+    // float flux = pmModelCP_GetFlux_Old (cp, dx, dy, theta);
+    float flux = pmModelCP_GetFlux_RotSquare (cp, dx, dy, theta);
+    
+    // RotSquare for theta = 0.0 & Bresen give the same answer 
+    // if I count from x[0] <= ix < x[1]
+
+# if (TEST_IMAGE) 
+    psFits *fits = psFitsOpen ("map.fits", "w");
+    psFitsWriteImage (fits, NULL, map, 0, NULL);
+    psFitsClose (fits);
+    psFree (map);
+# endif
+
+    return flux;
+}
+
+float pmModelCP_GetFlux_Old (pmModelCP *cp, float dx, float dy, float theta) {
+
+    // the cp data is defined for the central 3x3 pixels.  we allow dx,dy to have values of
+    // -1.0 <= dx,dy <= +1.0
+
+    // Xsub = (Xim * cos(theta) - Yim * sin(theta) + 1.5) * Nsub 
+    // Ysub = (Yim * cos(theta) + Xim * sin(theta) + 1.5) * Nsub 
+    
+    // integrate from (dx - 0.5 to dx + 0.5), (dy - 0.5 to dy + 0.5), 
+
+    // get the Xsub,Ysub values for the 4 corners, find the Xmin,Xmax, Ymin,Ymax in the
+    // subrastered image
+
+    float cs = cos(theta*PS_RAD_DEG);
+    float sn = sin(theta*PS_RAD_DEG);
+
+    float Nsub = 11.0;
+    int Xsub00 = ((dx - 0.5)*cs - (dy - 0.5)*sn + 1.5)*Nsub;
+    int Ysub00 = ((dx - 0.5)*sn + (dy - 0.5)*cs + 1.5)*Nsub;
+    int Xsub01 = ((dx - 0.5)*cs - (dy + 0.5)*sn + 1.5)*Nsub;
+    int Ysub01 = ((dx - 0.5)*sn + (dy + 0.5)*cs + 1.5)*Nsub;
+    int Xsub10 = ((dx + 0.5)*cs - (dy - 0.5)*sn + 1.5)*Nsub;
+    int Ysub10 = ((dx + 0.5)*sn + (dy - 0.5)*cs + 1.5)*Nsub;
+    int Xsub11 = ((dx + 0.5)*cs - (dy + 0.5)*sn + 1.5)*Nsub;
+    int Ysub11 = ((dx + 0.5)*sn + (dy + 0.5)*cs + 1.5)*Nsub;
+
+    int Xmin, Xmax, Ymin, Ymax;
+
+    Xmin = PS_MIN(Xsub00,Xsub01);
+    Xmin = PS_MIN(Xsub10,Xmin);
+    Xmin = PS_MIN(Xsub11,Xmin);
+    Xmin = PS_MIN(Xmin, cp->flux->numCols - 1);
+    Xmin = PS_MAX(Xmin, 0);
+    Xmax = PS_MAX(Xsub00,Xsub01);
+    Xmax = PS_MAX(Xsub10,Xmax);
+    Xmax = PS_MAX(Xsub11,Xmax);
+    Xmax = PS_MIN(Xmax, cp->flux->numCols - 1);
+    Xmax = PS_MAX(Xmax, 0);
+    Ymin = PS_MIN(Ysub00,Ysub01);
+    Ymin = PS_MIN(Ysub10,Ymin);
+    Ymin = PS_MIN(Ysub11,Ymin);
+    Ymin = PS_MIN(Ymin, cp->flux->numRows - 1);
+    Ymin = PS_MAX(Ymin, 0);
+    Ymax = PS_MAX(Ysub00,Ysub01);
+    Ymax = PS_MAX(Ysub10,Ymax);
+    Ymax = PS_MAX(Ysub11,Ymax);
+    Ymax = PS_MIN(Ymax, cp->flux->numRows - 1);
+    Ymax = PS_MAX(Ymax, 0);
+
+    // integrate pixels from Xmin,Ymin to Xmax,Ymax, only include pixels contained in the
+    // target pixel
+
+    float flux = 0.0;
+    int   npix = 0;
+    for (int i = Xmin; i < Xmax; i++) {
+	float dX = i / Nsub - 1.5;
+	for (int j = Ymin; j < Ymax; j++) {
+	    float dY = j / Nsub - 1.5;
+
+	    float Xim =  dX*cs + dY*sn;
+	    if (Xim < (dx - 0.5)) continue;
+	    if (Xim > (dx + 0.5)) continue;
+
+	    float Yim = -dX*sn + dY*cs;
+	    if (Yim < (dy - 0.5)) continue;
+	    if (Yim > (dy + 0.5)) continue;
+
+	    flux += cp->flux->data.F32[j][i];
+	    npix ++;
+	}
+    }
+	   
+    float normFlux = flux / npix;
+    return normFlux;
+}
+
+// *** pmSourceRadialProfileSortPair is a utility function for sorting a pair of vectors
+# define COMPARE_INDEX(A,B) (y[A] < y[B])
+# define SWAP_INDEX(TYPE,A,B) {				\
+	int tmp;					\
+	if (A != B) {					\
+	    tmp = x[A];					\
+	    x[A] = x[B];				\
+	    x[B] = tmp;					\
+	    tmp = y[A];					\
+	    y[A] = y[B];				\
+	    y[B] = tmp;					\
+	}						\
+    }
+
+bool pmModelCP_SortCorners (int *x, int *y, int Npar) {
+
+    if (Npar < 2) return true;
+
+    // sort the vector set by the radius
+    PSSORT (Npar, COMPARE_INDEX, SWAP_INDEX, NONE);
+    return true;
+}
+
+float pmModelCP_GetFlux_RotSquare (pmModelCP *cp, float dx, float dy, float theta) {
+
+    // the cp data is defined for the central 3x3 pixels.  we allow dx,dy to have values of
+    // -1.0 <= dx,dy <= +1.0
+
+    // Xsub = (Xim * cos(theta) - Yim * sin(theta) + 1.5) * Nsub 
+    // Ysub = (Yim * cos(theta) + Xim * sin(theta) + 1.5) * Nsub 
+    
+    // integrate from (dx - 0.5 to dx + 0.5), (dy - 0.5 to dy + 0.5), 
+
+    // get the Xsub,Ysub values for the 4 corners, find the Xmin,Xmax, Ymin,Ymax in the
+    // subrastered image
+
+    float cs = cos(theta*PS_RAD_DEG);
+    float sn = sin(theta*PS_RAD_DEG);
+    float Nsub = 11.0;
+
+    int Xsub[4], Ysub[4];
+
+    Xsub[0] = ((dx - 0.5)*cs - (dy - 0.5)*sn + 1.5)*Nsub;
+    Ysub[0] = ((dx - 0.5)*sn + (dy - 0.5)*cs + 1.5)*Nsub;
+    Xsub[1] = ((dx - 0.5)*cs - (dy + 0.5)*sn + 1.5)*Nsub;
+    Ysub[1] = ((dx - 0.5)*sn + (dy + 0.5)*cs + 1.5)*Nsub;
+    Xsub[2] = ((dx + 0.5)*cs - (dy - 0.5)*sn + 1.5)*Nsub;
+    Ysub[2] = ((dx + 0.5)*sn + (dy - 0.5)*cs + 1.5)*Nsub;
+    Xsub[3] = ((dx + 0.5)*cs - (dy + 0.5)*sn + 1.5)*Nsub;
+    Ysub[3] = ((dx + 0.5)*sn + (dy + 0.5)*cs + 1.5)*Nsub;
+
+    // first, sort the corners in order of the Y coordinate:
+    pmModelCP_SortCorners (Xsub, Ysub, 4);
+
+    float flux = 0.0;
+    float npix = 0.0;
+
+    // if (Ysub[0] == Ysub[1]), we have a simple square
+    if (Ysub[0] == Ysub[1]) {
+	psAssert (Ysub[2] == Ysub[3], "not square?");
+	int Xmin = PS_MIN(Xsub[0], Xsub[1]);
+	int Xmax = PS_MAX(Xsub[0], Xsub[1]);
+	for (int iy = Ysub[0]; iy < Ysub[3]; iy++) {
+	    for (int ix = Xmin; ix < Xmax; ix++) {
+		flux += cp->flux->data.F32[iy][ix];
+		npix += 1.0;
+# if (TEST_IMAGE) 
+		fprintf (stderr, "%d %d | %f %f | %f\n", ix, iy, flux, npix, cp->flux->data.F32[iy][ix]);
+		map->data.S32[iy][ix] ++;
+# endif
+	    }
+	}
+	float normFlux = flux / npix;
+	return normFlux;
+    }
+    
+    // second case: Xsub[1] > Xsub[2]:
+    if (Xsub[1] > Xsub[2]) {
+	float dYdXp, dYdXm;
+	// first segment, Ysub[0] to Ysub[1]:
+	dYdXp = (Ysub[1] - Ysub[0]) / (float) (Xsub[1] - Xsub[0]);
+	dYdXm = (Ysub[2] - Ysub[0]) / (float) (Xsub[2] - Xsub[0]);
+	for (int iy = Ysub[0]; iy < Ysub[1]; iy++) {
+	    int Xs = (iy - Ysub[0]) / dYdXm + Xsub[0];
+	    int Xe = (iy - Ysub[0]) / dYdXp + Xsub[0];
+	    for (int ix = Xs; ix < Xe; ix ++) {
+		flux += cp->flux->data.F32[iy][ix];
+		npix += 1.0;
+# if (TEST_IMAGE) 
+		fprintf (stderr, "%d %d | %f %f | %f\n", ix, iy, flux, npix, cp->flux->data.F32[iy][ix]);
+		map->data.S32[iy][ix] ++;
+# endif
+	    }
+	}
+	// 2nd segment, Ysub[1] to Ysub[2]:
+	dYdXp = (Ysub[3] - Ysub[1]) / (float) (Xsub[3] - Xsub[1]);
+	dYdXm = (Ysub[2] - Ysub[0]) / (float) (Xsub[2] - Xsub[0]);
+	for (int iy = Ysub[1]; iy < Ysub[2]; iy++) {
+	    int Xs = (iy - Ysub[0]) / dYdXm + Xsub[0];
+	    int Xe = (iy - Ysub[1]) / dYdXp + Xsub[1];
+	    for (int ix = Xs; ix < Xe; ix ++) {
+		flux += cp->flux->data.F32[iy][ix];
+		npix += 1.0;
+# if (TEST_IMAGE) 
+		fprintf (stderr, "%d %d | %f %f | %f\n", ix, iy, flux, npix, cp->flux->data.F32[iy][ix]);
+		map->data.S32[iy][ix] ++;
+# endif
+	    }
+	}
+	// first segment, Ysub[0] to Ysub[1]:
+	dYdXp = (Ysub[3] - Ysub[1]) / (float) (Xsub[3] - Xsub[1]);
+	dYdXm = (Ysub[3] - Ysub[2]) / (float) (Xsub[3] - Xsub[2]);
+	for (int iy = Ysub[2]; iy < Ysub[3]; iy++) {
+	    int Xs = (iy - Ysub[2]) / dYdXm + Xsub[2];
+	    int Xe = (iy - Ysub[1]) / dYdXp + Xsub[1];
+	    for (int ix = Xs; ix < Xe; ix ++) {
+		flux += cp->flux->data.F32[iy][ix];
+		npix += 1.0;
+# if (TEST_IMAGE) 
+		fprintf (stderr, "%d %d | %f %f | %f\n", ix, iy, flux, npix, cp->flux->data.F32[iy][ix]);
+		map->data.S32[iy][ix] ++;
+# endif
+	    }
+	}
+	float normFlux = flux / npix;
+	return normFlux;
+    }
+
+    // third case: Xsub[1] < Xsub[2]:
+    if (Xsub[2] > Xsub[1]) {
+	// first segment, Ysub[0] to Ysub[1]:
+	float dYdXp, dYdXm;
+	dYdXp = (Ysub[2] - Ysub[0]) / (float) (Xsub[2] - Xsub[0]);
+	dYdXm = (Ysub[1] - Ysub[0]) / (float) (Xsub[1] - Xsub[0]);
+	for (int iy = Ysub[0]; iy < Ysub[1]; iy++) {
+	    int Xs = (iy - Ysub[0]) / dYdXm + Xsub[0];
+	    int Xe = (iy - Ysub[0]) / dYdXp + Xsub[0];
+	    for (int ix = Xs; ix < Xe; ix ++) {
+		flux += cp->flux->data.F32[iy][ix];
+		npix += 1.0;
+# if (TEST_IMAGE) 
+		fprintf (stderr, "%d %d | %f %f | %f\n", ix, iy, flux, npix, cp->flux->data.F32[iy][ix]);
+		map->data.S32[iy][ix] ++;
+# endif
+	    }
+	}
+	// 2nd segment, Ysub[1] to Ysub[2]:
+	dYdXp = (Ysub[2] - Ysub[0]) / (float) (Xsub[2] - Xsub[0]);
+	dYdXm = (Ysub[3] - Ysub[1]) / (float) (Xsub[3] - Xsub[1]);
+	for (int iy = Ysub[1]; iy < Ysub[2]; iy++) {
+	    int Xs = (iy - Ysub[1]) / dYdXm + Xsub[1];
+	    int Xe = (iy - Ysub[0]) / dYdXp + Xsub[0];
+	    for (int ix = Xs; ix < Xe; ix ++) {
+		flux += cp->flux->data.F32[iy][ix];
+		npix += 1.0;
+# if (TEST_IMAGE) 
+		fprintf (stderr, "%d %d | %f %f | %f\n", ix, iy, flux, npix, cp->flux->data.F32[iy][ix]);
+		map->data.S32[iy][ix] ++;
+# endif
+	    }
+	}
+	// first segment, Ysub[0] to Ysub[1]:
+	dYdXp = (Ysub[3] - Ysub[2]) / (float) (Xsub[3] - Xsub[2]);
+	dYdXm = (Ysub[3] - Ysub[1]) / (float) (Xsub[3] - Xsub[1]);
+	for (int iy = Ysub[2]; iy < Ysub[3]; iy++) {
+	    int Xs = (iy - Ysub[1]) / dYdXm + Xsub[1];
+	    int Xe = (iy - Ysub[2]) / dYdXp + Xsub[2];
+	    for (int ix = Xs; ix < Xe; ix ++) {
+		flux += cp->flux->data.F32[iy][ix];
+		npix += 1.0;
+# if (TEST_IMAGE) 
+		fprintf (stderr, "%d %d | %f %f | %f\n", ix, iy, flux, npix, cp->flux->data.F32[iy][ix]);
+		map->data.S32[iy][ix] ++;
+# endif
+	    }
+	}
+	float normFlux = flux / npix;
+	return normFlux;
+    }
+    myAbort ("impossible case?");
+}
+
+float pmModelCP_GetFlux_Bresen (pmModelCP *cp, float dx, float dy, float theta) {
+
+    // the cp data is defined for the central 3x3 pixels.  we allow dx,dy to have values of
+    // -1.0 <= dx,dy <= +1.0
+
+    // Xsub = ( Xim * cos(theta) + Yim * sin(theta) + 1.5) * Nsub 
+    // Ysub = (-Yim * cos(theta) + Xim * sin(theta) + 1.5) * Nsub 
+    
+    // integrate from (dx - 0.5 to dx + 0.5), (dy - 0.5 to dy + 0.5), 
+
+    // get the Xsub,Ysub values for the 4 corners, find the Xmin,Xmax, Ymin,Ymax in the
+    // subrastered image
+
+    float cs = cos(theta*PS_RAD_DEG);
+    float sn = sin(theta*PS_RAD_DEG);
+
+    float Nsub = 11.0;
+    int Xsub00 = 0.5 + ((dx - 0.5)*cs + (dy - 0.5)*sn + 1.5)*Nsub;
+    int Ysub00 = 0.5 + ((dy - 0.5)*cs - (dx - 0.5)*sn + 1.5)*Nsub;
+    int Xsub01 = 0.5 + ((dx - 0.5)*cs + (dy + 0.5)*sn + 1.5)*Nsub;
+    int Ysub01 = 0.5 + ((dy + 0.5)*cs - (dx - 0.5)*sn + 1.5)*Nsub;
+    int Xsub10 = 0.5 + ((dx + 0.5)*cs + (dy - 0.5)*sn + 1.5)*Nsub;
+    int Ysub10 = 0.5 + ((dy - 0.5)*cs - (dx + 0.5)*sn + 1.5)*Nsub;
+    int Xsub11 = 0.5 + ((dx + 0.5)*cs + (dy + 0.5)*sn + 1.5)*Nsub;
+    int Ysub11 = 0.5 + ((dy + 0.5)*cs - (dx + 0.5)*sn + 1.5)*Nsub;
+
+    float flux = pmModelCP_GetFlux_BresenSquare (cp, Xsub00, Ysub00, Xsub10, Ysub10, Xsub01, Ysub01, Xsub11, Ysub11);
+    return flux;
+}
+
+// first line is (X00,Y00) - (X10,Y1) : last line is (X01,Y01) - (X11,Y11)
+float pmModelCP_GetFlux_BresenSquare (pmModelCP *cp, int X00, int Y00, int X10, int Y10, int X01, int Y01, int X11, int Y11) {
+
+    int dX0 = X01 - X00;
+    int dY0 = Y01 - Y00;
+
+    // int dX1 = X11 - X10;
+    // int dY1 = Y11 - Y10;
+
+    // myAssert ((dX0 == dX1) && (dY0 == dY1), "pixel is not square?");
+
+    bool FlipCoords = (abs(dX0) < abs(dY0));
+    bool FlipDirect = FlipCoords ? (Y00 > Y10) : (X00 > X10);
+
+    float flux = 0.0;
+    if (!FlipDirect && !FlipCoords) flux = pmModelCP_GetFlux_BresenSquareBase (cp, X00, Y00, X10, Y10, X01, Y01, X11, Y11, false);
+    if ( FlipDirect && !FlipCoords) flux = pmModelCP_GetFlux_BresenSquareBase (cp, X10, Y10, X00, Y00, X11, Y11, X01, Y01, false);
+    if (!FlipDirect &&  FlipCoords) flux = pmModelCP_GetFlux_BresenSquareBase (cp, Y00, X00, Y10, X10, Y01, X01, Y11, X11, true);
+    if ( FlipDirect &&  FlipCoords) flux = pmModelCP_GetFlux_BresenSquareBase (cp, Y10, X10, Y00, X00, Y11, X11, Y01, X01, true);
+    return flux;
+}
+
+// draw a line between (X00,Y00) & (X01,Y01) and increment to the next line segment until endpoints (X10,Y10) & (X11,Y11)
+float pmModelCP_GetFlux_BresenSquareBase (pmModelCP *cp, int X00, int Y00, int X10, int Y10, int X01, int Y01, int X11, int Y11, bool swapcoords) {
+
+    int dX0 = X01 - X00;
+    int dY0 = Y01 - Y00;
+
+    // int dX1 = X11 - X10;
+    // int dY1 = Y11 - Y10;
+
+    // myAssert ((dX0 == dX1) && (dY0 == dY1), "pixel is not square?");
+
+    float flux = 0.0;
+    float npix = 0.0;
+
+    int Ys = Y00;
+    int Ye = Y10;
+    int e = 0;
+    for (int Xs = X00, Xe = X10; Xs < X01; Xs++, Xe++) {
+	if (swapcoords) {
+	    pmModelCP_GetFlux_BresenLine (&flux, &npix, cp, Ys, Xs, Ye, Xe);
+	} else {
+	    pmModelCP_GetFlux_BresenLine (&flux, &npix, cp, Xs, Ys, Xe, Ye);
+	}
+	e += dY0;
+	float e2 = 2 * e;
+	if (e2 > dX0) {
+	    Ys++;
+	    Ye++;
+	    e -= dX0;
+	} 
+	if (e2 < -dX0) {
+	    Ys--;
+	    Ye--;
+	    e += dX0;
+	}
+    }
+    float normFlux = flux / npix;
+    // fprintf (stderr, "bres: %f %f %f\n", flux, (float) npix, normFlux);
+    return normFlux;
+}
+
+// get the sequence right: 
+// if abs(dY) > abs(dX) : we will run in the Y direction not the X direction (we swap X and Y going in)
+// if the direction (dX or dY) is negative, go the opposite direction
+bool pmModelCP_GetFlux_BresenLine (float *flux, float *npix, pmModelCP *cp, int X0, int Y0, int X1, int Y1) {
+
+  int dX = X1 - X0;
+  int dY = Y1 - Y0;
+
+  bool FlipCoords = (abs(dX) < abs(dY));
+  bool FlipDirect = FlipCoords ? (Y0 > Y1) : (X0 > X1);
+
+  if (!FlipDirect && !FlipCoords) pmModelCP_GetFlux_BresenLineBase (flux, npix, cp, X0, Y0, X1, Y1, false);
+  if ( FlipDirect && !FlipCoords) pmModelCP_GetFlux_BresenLineBase (flux, npix, cp, X1, Y1, X0, Y0, false);
+  if (!FlipDirect &&  FlipCoords) pmModelCP_GetFlux_BresenLineBase (flux, npix, cp, Y0, X0, Y1, X1, true);
+  if ( FlipDirect &&  FlipCoords) pmModelCP_GetFlux_BresenLineBase (flux, npix, cp, Y1, X1, Y0, X0, true);
+  return true;
+}
+
+bool pmModelCP_GetFlux_BresenLineBase (float *flux, float *npix, pmModelCP *cp, int X0, int Y0, int X1, int Y1, bool swapcoords) {
+
+    int dX = X1 - X0;
+    int dY = Y1 - Y0;
+
+    int Y = Y0;
+    int e = 0;
+    for (int X = X0; X < X1; X++) {
+	if (swapcoords) {
+	    *flux += cp->flux->data.F32[X][Y];
+	    *npix += 1.0;
+# if (TEST_IMAGE) 
+	    fprintf (stderr, "%d %d | %f %f | %f\n", X, Y, *flux, *npix, cp->flux->data.F32[X][Y]);
+	    map->data.S32[X][Y] ++;
+# endif
+	} else {
+	    *flux += cp->flux->data.F32[Y][X];
+	    *npix += 1.0;
+# if (TEST_IMAGE) 
+	    fprintf (stderr, "%d %d | %f %f | %f\n", X, Y, *flux, *npix, cp->flux->data.F32[Y][X]);
+	    map->data.S32[Y][X] ++;
+# endif
+	}
+	e += dY;
+	float e2 = 2 * e;
+	if (e2 > dX) {
+	    Y++;
+	    e -= dX;
+	} 
+	if (e2 < -dX) {
+	    Y--;
+	    e += dX;
+	}
+    }
+    return true;
+}
+
+// this is a test function which generates a full sersic model evaluation with sub-pixel sampling
+float pmModelCP_FullSersic (float dx, float dy, float theta, float Rmajor, float Aratio, float Sindex) {
+
+    float flux = 0.0;
+    int   npix = 0;
+
+    float Rminor = Aratio * Rmajor;
+    float f1 = 1.0 / PS_SQR(Rminor) + 1.0 / PS_SQR(Rmajor);
+    float f2 = 1.0 / PS_SQR(Rminor) - 1.0 / PS_SQR(Rmajor);
+    
+    float sxr = 0.5*f1 - 0.5*f2*cos(2.0*theta*PS_RAD_DEG);
+    float syr = 0.5*f1 + 0.5*f2*cos(2.0*theta*PS_RAD_DEG);
+    
+    float Rxx  = +1.0 / sqrt(sxr);
+    float Ryy  = +1.0 / sqrt(syr);
+    float Rxy = -f2*sin(2.0*theta*PS_RAD_DEG);
+    
+    float kappa = -0.275552 + 1.972625*Sindex + 0.003487 * PS_SQR(Sindex);
+    float rindex = 0.5 / Sindex;
+
+    float off = -60.0/(11*11);
+    float delta = 1.0 / (11*11);
+    for (float ix = off; ix < 0.5; ix += delta) {
+	for (float iy = off; iy < 0.5; iy += delta) {
+
+	    float dX = dx + ix;
+	    float dY = dy + iy;
+	    float z = PS_SQR(dX / Rxx) + PS_SQR(dY / Ryy) + dX * dY * Rxy;
+
+	    float q = pow (z, rindex);
+	    float f = exp(-kappa*q);
+
+	    flux += f;
+	    npix ++;
+	}
+    }
+    float normFlux = flux / npix;
+    // fprintf (stderr, "full : %f %f %f\n", flux, (float) npix, normFlux);
+    return normFlux;
+}
+
+// this is a test function which generates a full sersic model evaluation with sub-pixel sampling
+// Nsub is the number of sub-pixel samplings and must be odd
+// dx,dy are the centroid offset
+float pmModelCP_SersicSubpix (float dx, float dy, float Rxx, float Rxy, float Ryy, float Sindex, int Nsub) {
+
+    float flux = 0.0;
+    int   npix = 0;
+
+    // -0.275552 + 1.972625*Sindex + 0.003487 * PS_SQR(Sindex);
+    float kappa = pmSersicKappa (Sindex);
+    float rindex = 0.5 / Sindex;
+
+    // have the resolution be a user-parameter?
+    psAssert (Nsub % 2 == 1, "Nsub is not odd");
+    int Nsub2 = (Nsub - 1) / 2;
+
+    float delta = 1.0 / (float) Nsub;
+    // float off = -Nsub2 * delta;
+
+    int Sx = (int) floor(dx / delta);
+    int Sy = (int) floor(dy / delta);
+
+    for (int ix = -Nsub2; ix <= Nsub2; ix++) {
+      float dX = delta * (Sx + ix);
+      for (int iy = -Nsub2; iy <= Nsub2; iy++) {
+	float dY = delta * (Sy + iy);
+	    float z = PS_SQR(dX / Rxx) + PS_SQR(dY / Ryy) + dX * dY * Rxy;
+
+	    float q = pow (z, rindex);
+	    float f = exp(-kappa*q);
+
+	    // if ((ix == 0) && (iy == 0)) {
+	    //   // fprintf (stderr, "this: %f  %f  %f  --  full : %f %f\n", z, q, f, flux, (float) npix);
+	    // }
+
+	    flux += f;
+	    npix ++;
+	}
+    }
+    float normFlux = flux / npix;
+    // fprintf (stderr, "full : %f %f %f\n", flux, (float) npix, normFlux);
+    return normFlux;
+}
+
+float pmSersicKappa (float Sindex) {
+    // this function is empirically derived from a fit to data for Sindex = 0.5 - 5.5
+    // constrain Sindex or kappa?
+    float kappa = -0.275552 + 1.972625*Sindex + 0.003487 * PS_SQR(Sindex);
+    return kappa;
+}
+
+float pmSersicNorm (float Sindex) {
+
+    float C0 = NAN;
+    float C1 = NAN;
+    float C2 = NAN;
+
+    // y = 0.201545 x^0 -0.950965 x^1 -0.315248 x^2 
+    // y = 0.402084 x^0 -1.357775 x^1 -0.105102 x^2 
+    // y = 0.619093 x^0 -1.591674 x^1 -0.041576 x^2 
+    // y = 0.770263 x^0 -1.696421 x^1 -0.023363 x^2 
+    // y = 0.885891 x^0 -1.755684 x^1 -0.015753 x^2 
+
+    if ((Sindex >= 0.0) && (Sindex < 1.0)) { 
+	C0 = 0.201545; C1 = -0.950965; C2 = -0.315248;
+	// y = 0.201545 x^0 -0.950965 x^1 -0.315248 x^2 
+    }
+    if ((Sindex >= 1.0) && (Sindex < 2.0)) { 
+	C0 = 0.402084; C1 = -1.357775; C2 = -0.105102;
+	// y = 0.402084 x^0 -1.357775 x^1 -0.105102 x^2 
+    }
+    if ((Sindex >= 2.0) && (Sindex < 3.0)) { 
+	C0 = 0.619093; C1 = -1.591674; C2 = -0.041576;
+	// y = 0.619093 x^0 -1.591674 x^1 -0.041576 x^2 
+    }
+    if ((Sindex >= 3.0) && (Sindex < 4.0)) { 
+	C0 = 0.770263; C1 = -1.696421; C2 = -0.023363;
+	// y = 0.770263 x^0 -1.696421 x^1 -0.023363 x^2 
+    }
+    if ((Sindex >= 4.0) && (Sindex < 5.5)) { 
+	C0 = 0.885891; C1 = -1.755684; C2 = -0.015753; 
+	// y = 0.885891 x^0 -1.755684 x^1 -0.015753 x^2 
+    }
+
+    if (isnan(C0)) return NAN;
+
+    float lnorm = C0 + C1*Sindex + C2*Sindex*Sindex;
+    float norm = exp(lnorm);
+    return norm;
+}
+
+# if (0)
+// create a vector containing only the unique entries in the input vector
+psVector *psVectorUniqueSubset (psVector *input) {
+
+    // sort the input vector (to new temp vector)
+    // run through the sorted vector, copying to a new output vector if the current value is
+    // new
+
+    psVector *temp = psVectorSort (input);
+    
+    psVector *output = psVectorAllocEmpty (0.5*input->n, PS_TYPE_F32);
+    
+    psVectorAppend (output, temp->data.F32[0]);
+    float lastValue = temp->data.F32[0];
+    for (int i = 0; i < temp->n; i++) {
+	if (temp->data.F32[i] == lastValue) continue;
+	psVectorAppend (output, temp->data.F32[i]);
+	float lastValue = temp->data.F32[i];
+    }
+    psFree (temp);
+    return output;
+}
+
+getUnique() {
+    // we need to convert the collection of Rmajor, Aratio, Sindex values to a cube
+    // such that entry[RmajorBin][AratioBin][SindexBin] is the CPset array element
+
+    // create full vectors will all Rmajor, Aratio, Sindex values:
+    psVector *RmajorAll = psVectorAllocEmpty (CPset->n, PS_TYPE_F32);
+    psVector *AratioAll = psVectorAllocEmpty (CPset->n, PS_TYPE_F32);
+    psVector *SindexAll  = psVectorAllocEmpty (CPset->n, PS_TYPE_F32);
+    for (int i = 0; i < CPset->n; i++) {
+	pmModelCP *cp = CPset->data[i];
+	psVectorAppend (RmajorAll, cp->Rmajor);
+	psVectorAppend (AratioAll, cp->Aratio);
+	psVectorAppend (SindexAll,  cp->Sindex);
+    }
+    psVector *RmajorUniq = psVectorUniqueSubset (RmajorAll);
+    psVector *AratioUniq = psVectorUniqueSubset (AratioAll);
+    psVector *SindexUniq  = psVectorUniqueSubset (SindexAll);
+}
+# endif
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmModel_CentralPixel.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmModel_CentralPixel.h	(revision 37066)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmModel_CentralPixel.h	(revision 37066)
@@ -0,0 +1,69 @@
+/* @file  pmModel_CentralPixel.h
+ * @brief Functions to manage the central pixel for sersic-like models
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.19 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2009-02-16 22:30:50 $
+ *
+ * Copyright 2013 Institute for Astronomy, University of Hawaii
+ */
+
+# ifndef PM_MODEL_CENTRAL_PIXEL_H
+# define PM_MODEL_CENTRAL_PIXEL_H
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+typedef struct {
+    psImage *flux;
+    float Rmajor;
+    float Aratio;
+    float Sindex;
+} pmModelCP;
+
+typedef struct {
+    psArray *images;
+
+    float RmajorMin;
+    float RmajorMax;
+    float RmajorDel;
+
+    float AratioMin;
+    float AratioMax;
+    float AratioDel;
+
+    float SindexMin;
+    float SindexMax;
+    float SindexDel;
+
+    int RmajorNitem;
+    int AratioNitem;
+    int SindexNitem;
+
+    int ***lookupCube;
+
+} pmModelCPset;
+
+pmModelCP    *pmModelCP_Alloc(void);
+pmModelCPset *pmModelCPset_Alloc(void);
+
+pmModelCPset *pmModelCP_Load (char *filename);
+
+pmModelCP    *pmModelCP_GetImage (pmModelCPset *CPset, float Rmajor, float Aratio, float Sindex);
+
+float         pmModelCP_GetFlux (pmModelCP *cp, float dx, float dy, float theta);
+float         pmModelCP_FullSersic (float dx, float dy, float theta, float Rmajor, float Aratio, float Sindex);
+
+bool pmModelCP_GetFlux_BresenLineBase (float *flux, float *npix, pmModelCP *cp, int X0, int Y0, int X1, int Y1, bool swapcoords);
+bool pmModelCP_GetFlux_BresenLine (float *flux, float *npix, pmModelCP *cp, int X0, int Y0, int X1, int Y1);
+float pmModelCP_GetFlux_BresenSquareBase (pmModelCP *cp, int X00, int Y00, int X01, int Y01, int X10, int Y10, int X11, int Y11, bool swapcoords);
+float pmModelCP_GetFlux_BresenSquare (pmModelCP *cp, int X00, int Y00, int X01, int Y01, int X10, int Y10, int X11, int Y11);
+float pmModelCP_GetFlux_Bresen (pmModelCP *cp, float dx, float dy, float theta);
+float pmModelCP_GetFlux_Old (pmModelCP *cp, float dx, float dy, float theta);
+
+float pmModelCP_SersicSubpix (float dx, float dy, float Rxx, float Rxy, float Ryy, float Sindex, int Nsub);
+
+float pmSersicKappa (float Sindex);
+float pmSersicNorm (float Sindex);
+
+# endif
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmMoments.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmMoments.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmMoments.h	(revision 37066)
@@ -44,4 +44,6 @@
     float Myyyy;   ///< fourth moment
 
+  // float wSum;    ///< window-weighted sum (NOT needed by lensing)
+
     float Sum;    ///< Pixel sum above sky (background).
     float Peak;   ///< Peak counts above sky.
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPCM_MinimizeChisq.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPCM_MinimizeChisq.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPCM_MinimizeChisq.c	(revision 37066)
@@ -31,14 +31,20 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
 #include "pmPCMdata.h"
+
+# define SAVE_IMAGES 0
+# if (SAVE_IMAGES) 
+int psphotSaveImage (psMetadata *header, psImage *image, char *filename);
+# endif
 
 # define FACILITY "psModules.objects"
@@ -91,5 +97,5 @@
     psF32 lambda = 0.001;
     psF32 dLinear = 0.0;
-    psF32 nu = 2.0;
+    psF32 nu = 3.0;
 
 # if (USE_FFT && PRE_CONVOLVE)
@@ -130,6 +136,38 @@
 	}
 
+	if (min->isInteractive) {
+	    fprintf (stderr, "%d : ", min->iter);
+	    for (int ti = 0; ti < params->n; ti++) {
+		fprintf (stderr, "%f  ", params->data.F32[ti]);
+	    }
+	    fprintf (stderr, " : %f\n", min->value);
+	}
+
+	char key[10]; // used for interactive responses
+	bool testValue = false;
+
         // set a new guess for Alpha, Beta, Params
         if (!psMinLM_GuessABP(Alpha, Beta, Params, alpha, beta, params, paramMask, checkLimits, lambda, &dLinear)) {
+	    if (false && min->isInteractive) {
+		fprintf (stdout, "guess failed (singular matrix or NaN values), continue? [Y,n] ");
+		if (!fgets(key, 8, stdin)) {
+		    psWarning("Unable to read option");
+		}
+		switch (key[0]) {
+		  case 'n':
+		  case 'N':
+		    done = true;
+		    break;
+		  case 'y':
+		  case 'Y':
+		  case '\n':
+		    lambda *= 10.0;
+		    continue;
+		  default:
+		    lambda *= 10.0;
+		    continue;
+		}
+		if (done) break;
+	    }
             min->iter ++;
 	    if (min->iter >=  min->maxIter) break;
@@ -138,4 +176,40 @@
         }
 
+	if (false && min->isInteractive) {
+            p_psVectorPrint(psTraceGetDestination(), Params, "current parameters: ");
+	    fprintf (stdout, "last chisq : %f\n", min->value);
+	    bool getOptions = true;
+	    while (getOptions) {
+		fprintf (stdout, "options: (m)odify, (g)o, (q)uit: ");
+		if (!fgets(key, 8, stdin)) {
+		    psWarning("Unable to read option");
+		}
+		switch (key[0]) {
+		  case 'm':
+		  case 'M':
+		    testValue = TRUE;
+		    fprintf (stdout, "enter (Npar) (value): ");
+		    int Npar = 0;
+		    float value= 0;
+		    int Nscan = fscanf (stdin, "%d %f", &Npar, &value);
+		    if (Nscan != 2) {
+		      fprintf (stderr, "scan failure\n");
+		    }
+		    Params->data.F32[Npar] = value;
+		    break;
+		  case 'g':
+		  case 'G':
+		  case '\n':
+		    getOptions = false;
+		    break;
+		  default:
+		    done = true;
+		    break;
+		}
+		fprintf (stderr, "foo\n");
+	    }
+	    if (done) break;
+	}
+	    
         // dump some useful info if trace is defined
         if (psTraceGetLevel(FACILITY) >= 6) {
@@ -202,5 +276,5 @@
 	// XXX : Madsen gives suggestion for better use of rho
         // rho is positive if the new chisq is smaller
-        if (rho >= -1e-6) {
+        if (testValue || (rho >= -1e-6)) {
             min->value = Chisq;
             alpha  = psImageCopy(alpha, Alpha, PS_TYPE_F32);
@@ -215,5 +289,5 @@
 	  case 0:
 	    if (rho >= -1e-6) {
-		lambda *= 0.25;
+		lambda *= 0.1;
 	    } else {
 		lambda *= 10.0;
@@ -234,8 +308,8 @@
 	    if (rho > 0.0) {
 		lambda *= PS_MAX(0.33, (1.0 - pow(2.0*rho - 1.0, 3.0)));
-		nu = 2.0;
+		nu = 3.0;
 	    } else {
 		lambda *= nu;
-		nu *= 2.0;
+		nu *= 3.0;
 	    }
 	    break;
@@ -381,5 +455,5 @@
             coord->data.F32[1] = (psF32) (i + 0.5 + source->pixels->row0);
 
-            pcm->modelFlux->data.F32[i][j] = pcm->modelConv->modelFunc (deriv, params, coord);
+            pcm->modelFlux->data.F32[i][j] = pcm->modelConv->class->modelFunc (deriv, params, coord);
 
             for (int n = 0; n < params->n; n++) {
@@ -408,10 +482,15 @@
 # else
     if (pcm->use1Dgauss) {
-	// do not use the threaded, mask-aware version of this code (psImageSmoothMaskPixelsThread):
-	// * the model flux is not masked
-	// * threading takes place above this level
-	pcm->modelConvFlux = psImageCopy (pcm->modelConvFlux, pcm->modelFlux, pcm->modelFlux->type.type);
-	psImageSmooth_PreAlloc_F32 (pcm->modelConvFlux, pcm->smdata);
-	// psImageSmooth (pcm->modelConvFlux, pcm->sigma, pcm->nsigma);
+
+	if (USE_1D_CACHE) {
+	    // do not use the threaded, mask-aware version of this code (psImageSmoothMaskPixelsThread):
+	    // * the model flux is not masked
+	    // * threading takes place above this level
+	    pcm->modelConvFlux = psImageCopy (pcm->modelConvFlux, pcm->modelFlux, pcm->modelFlux->type.type);
+	    psImageSmoothCache_F32 (pcm->modelConvFlux, pcm->smdata);
+	} else {
+	    pcm->modelConvFlux = psImageCopy (pcm->modelConvFlux, pcm->modelFlux, pcm->modelFlux->type.type);
+	    psImageSmooth2dCache_F32 (pcm->modelConvFlux, pcm->smdata2d);
+	}
     } else {
 	psImageConvolveKernel (pcm->modelConvFlux, pcm->modelFlux, NULL, 0, pcm->psfFFT);
@@ -428,10 +507,14 @@
 # else
 	if (pcm->use1Dgauss) {
-	    // do not use the threaded, mask-aware version of this code (psImageSmoothMaskPixelsThread):
-	    // * the model flux is not masked
-	    // * threading takes place above this level
-	    dmodelConv = psImageCopy (dmodelConv, dmodel, dmodel->type.type);
-	    psImageSmooth_PreAlloc_F32 (dmodelConv, pcm->smdata);
-	    // psImageSmooth (dmodelConv, pcm->sigma, pcm->nsigma);
+	    if (USE_1D_CACHE) {
+		// do not use the threaded, mask-aware version of this code (psImageSmoothMaskPixelsThread):
+		// * the model flux is not masked
+		// * threading takes place above this level
+		dmodelConv = psImageCopy (dmodelConv, dmodel, dmodel->type.type);
+		psImageSmoothCache_F32 (dmodelConv, pcm->smdata);
+	    } else {
+		dmodelConv = psImageCopy (dmodelConv, dmodel, dmodel->type.type);
+		psImageSmooth2dCache_F32 (dmodelConv, pcm->smdata2d);
+	    }
 	} else {
 	    psImageConvolveKernel (dmodelConv, dmodel, NULL, 0, pcm->psfFFT);
@@ -449,10 +532,14 @@
 
 	if (pcm->use1Dgauss) {
-	    // do not use the threaded, mask-aware version of this code (psImageSmoothMaskPixelsThread):
-	    // * the model flux is not masked
-	    // * threading takes place above this level
-	    dmodelConv = psImageCopy (dmodelConv, dmodel, dmodel->type.type);
-	    psImageSmooth_PreAlloc_F32 (dmodelConv, pcm->smdata);
-	    // psImageSmooth (dmodelConv, pcm->sigma, pcm->nsigma);
+	    if (USE_1D_CACHE) {
+		// do not use the threaded, mask-aware version of this code (psImageSmoothMaskPixelsThread):
+		// * the model flux is not masked
+		// * threading takes place above this level
+		dmodelConv = psImageCopy (dmodelConv, dmodel, dmodel->type.type);
+		psImageSmoothCache_F32 (dmodelConv, pcm->smdata);
+	    } else {
+		dmodelConv = psImageCopy (dmodelConv, dmodel, dmodel->type.type);
+		psImageSmooth2dCache_F32 (dmodelConv, pcm->smdata2d);
+	    }
 	} else {
 	    psImageConvolveFFT (dmodelConv, dmodel, NULL, 0, pcm->psf);
@@ -474,10 +561,20 @@
     // 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");
+    static int Npass = 0;
+    char name[128]; 
+    if (!pcm->use1Dgauss) {
+      snprintf (name, 128, "psf.%03d.fits", Npass); psphotSaveImage (NULL, pcm->psf->image, name);
+    }
+    snprintf (name, 128, "mod.%03d.fits", Npass); psphotSaveImage (NULL, pcm->modelFlux, name);
+    snprintf (name, 128, "cnv.%03d.fits", Npass); psphotSaveImage (NULL, pcm->modelConvFlux, name);
+    snprintf (name, 128, "obj.%03d.fits", Npass); psphotSaveImage (NULL, source->pixels, name);
+    snprintf (name, 128, "msk.%03d.fits", Npass); psphotSaveImage (NULL, source->maskObj, name);
+    snprintf (name, 128, "var.%03d.fits", Npass); psphotSaveImage (NULL, source->variance, name);
+    for (int n = 0; n < pcm->dmodelsFlux->n; n++) {
+        psImage *dmodelConv = pcm->dmodelsConvFlux->data[n];
+	if (!dmodelConv) continue;
+	snprintf (name, 128, "dpar.%01d.%03d.fits", n, Npass); psphotSaveImage (NULL, dmodelConv, name);
+    }
+    Npass ++;
 # endif
 
@@ -506,5 +603,7 @@
 
             float ymodel  = pcm->modelConvFlux->data.F32[i][j];
-            float yweight = 1.0 / source->variance->data.F32[i][j];
+
+	    // XXXX note this point here:::
+            float yweight = pcm->poissonErrors ? 1.0 / source->variance->data.F32[i][j] : 1.0;
             float delta = ymodel - source->pixels->data.F32[i][j];
 
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPCMdata.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPCMdata.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPCMdata.c	(revision 37066)
@@ -31,11 +31,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -43,5 +44,4 @@
 
 # define USE_DELTA_PSF 0
-# define USE_1D_GAUSS 1
 
 static void pmPCMdataFree (pmPCMdata *pcm) {
@@ -58,5 +58,7 @@
     psFree (pcm->psfFFT);
     psFree (pcm->constraint);
+
     psFree (pcm->smdata); // pre-allocated data for psImageSmooth_PreAlloc
+    psFree (pcm->smdata2d); // pre-allocated data for psImageSmooth_PreAlloc
     return;
 }
@@ -88,4 +90,7 @@
     }
 
+    pcm->smdata = NULL;
+    pcm->smdata2d = NULL;
+
     pcm->modelConv = NULL;
     pcm->psf = NULL;
@@ -94,8 +99,10 @@
     pcm->nDOF = 0;
 
+    pcm->poissonErrors = true;
+
     // full convolution with the PSF is expensive.  if we have to save time, we can do a 1D
     // convolution with a Gaussian approximation to the kernel
     pcm->use1Dgauss = false;
-    pcm->nsigma = 3.0; 
+    pcm->nsigma = NAN; // this is set to something defined by the user
     pcm->sigma = 1.0; // this should be set to something sensible when the psf is known
 
@@ -173,21 +180,190 @@
 }
 
+int pmPCMsetParams (psMinConstraint *constraint, pmSourceFitMode mode) {
+
+    // set parameter mask based on fitting mode
+    int nParams = 0;
+    int nParAll = constraint->paramMask->n;
+
+    switch (mode) {
+      case PM_SOURCE_FIT_NORM:
+        // 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:
+        // fits only 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:
+        // fits all params except sky
+        nParams = nParAll - 1;
+        psVectorInit (constraint->paramMask, 0);
+        constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_SKY] = 1;
+        break;
+
+      case PM_SOURCE_FIT_EXT_AND_SKY:
+        // fits all params including sky
+        nParams = nParAll;
+        psVectorInit (constraint->paramMask, 0);
+        break;
+
+      case PM_SOURCE_FIT_SHAPE:
+	// fits shape (Sxx, Sxy, Syy) and Io
+	nParams = 5;
+	psVectorInit (constraint->paramMask, 1);
+	constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_SKY] = 0;
+	constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_I0] = 0;
+	constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_SXX] = 0;
+	constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_SXY] = 0;
+	constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_SYY] = 0;
+	break;
+
+      case PM_SOURCE_FIT_INDEX:
+        // fits only 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 (nParAll == 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:
+        // fits all but index (PAR7) including sky
+	psVectorInit (constraint->paramMask, 0);
+        if (nParAll == 7) {
+	    nParams = nParAll;
+	} else {
+	    nParams = nParAll - 1;
+	    constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_7] = 1;
+	}
+	break;
+      default:
+	psAbort("invalid fitting mode");
+    }
+    return nParams;
+}
+
+static int modelType_GAUSS = -1;
+static int modelType_PS1_V1 = -1;
+
+// generate a Gaussian smoothing kernel for supplied sigma.  sigma here does not need to match
+// that used to allocate the structure, but it is recommended
+bool psImageSmoothCacheKernel_PS1_V1 (psImageSmoothCacheData *smdata, float sigma, float kappa) {
+    // check for NULL structure elements?
+
+    int size = smdata->Nrange;
+
+    psFree (smdata->kernel);
+    smdata->kernel = psVectorAlloc(2 * smdata->Nrange + 1, PS_TYPE_F32);
+
+    double sum = 0.0;			// Sum of Gaussian, for normalization
+    double factor = 1.0 / (sigma * M_SQRT2);	// Multiplier for i -> z
+
+    // PS1_V1 is a power-law with fitted linear term:
+    // 1 / (1 + kappa z + z^1.666)  where z = (r/sigma)^2
+
+    // generate the kernel (not normalized)
+    for (int i = -size, j = 0; i <= size; i++, j++) {
+	float z = PS_SQR(i * factor);
+        sum += smdata->kernel->data.F32[j] = 1.0 / (1 + kappa * z + pow(z,1.666));
+    }
+
+    // renormalize kernel to integral of 1.0
+    for (int i = 0; i < 2 * size + 1; i++) {
+        smdata->kernel->data.F32[i] /= sum;
+    }
+
+    return true;
+}
+
+psImageSmoothCacheData *psImageSmoothCacheSetKernel (float *sigma, float *kappa, float nsigma, psImage *flux, pmModel *modelPSF) {
+
+    psAssert (modelPSF, "psf model must be defined");
+    
+    psEllipseAxes axes;
+    bool useReff = modelPSF->class->useReff;
+    psF32 *PAR = modelPSF->params->data.F32;
+    pmModelParamsToAxes (&axes, PAR[PM_PAR_SXX], PAR[PM_PAR_SXY], PAR[PM_PAR_SYY], useReff);
+    
+    *sigma = NAN;
+    *kappa = NAN;
+
+    // XXX need to do this more carefully
+    if (modelPSF->type == modelType_GAUSS) {
+	float FWHM_MAJOR = 2*modelPSF->class->modelRadius (modelPSF->params, 0.5*PAR[PM_PAR_I0]);
+	float FWHM_MINOR = FWHM_MAJOR * (axes.minor / axes.major);
+	*sigma = 0.50 * (FWHM_MAJOR + FWHM_MINOR) / 2.35;
+    }
+    if (modelPSF->type == modelType_PS1_V1) {
+	*sigma = 0.5 * (axes.major + axes.minor);
+	*kappa = PAR[PM_PAR_7];
+    }
+    psAssert (isfinite(*sigma), "invalid model type");
+
+    // psImageSmoothCacheAlloc generates a structure but does not assign the smoothing vector
+    psImageSmoothCacheData *smdata = psImageSmoothCacheAlloc (flux, *sigma, nsigma);
+
+    if (modelPSF->type == modelType_GAUSS) {
+	psImageSmoothCacheKernel_Gauss (smdata, *sigma);
+    }
+    if (modelPSF->type == modelType_PS1_V1) {
+	psImageSmoothCacheKernel_PS1_V1 (smdata, *sigma, *kappa);
+    }
+
+    return smdata;
+}
+
+psImageSmooth2dCacheData *psImageSmooth2dCacheSetKernel (float *sigma, float *kappa, float nsigma, psImage *flux, pmModel *modelPSF) {
+
+    psAssert (modelPSF, "psf model must be defined");
+    
+    psEllipseAxes axes;
+    bool useReff = modelPSF->class->useReff;
+    psF32 *PAR = modelPSF->params->data.F32;
+    pmModelParamsToAxes (&axes, PAR[PM_PAR_SXX], PAR[PM_PAR_SXY], PAR[PM_PAR_SYY], useReff);
+    
+    *sigma = NAN;
+    *kappa = NAN;
+
+    // XXX need to do this more carefully
+    if (modelPSF->type == modelType_GAUSS) {
+	float FWHM_MAJOR = 2*modelPSF->class->modelRadius (modelPSF->params, 0.5*PAR[PM_PAR_I0]);
+	float FWHM_MINOR = FWHM_MAJOR * (axes.minor / axes.major);
+	*sigma = 0.50 * (FWHM_MAJOR + FWHM_MINOR) / 2.35;
+    }
+    if (modelPSF->type == modelType_PS1_V1) {
+	*sigma = 0.5 * (axes.major + axes.minor);
+	*kappa = PAR[PM_PAR_7];
+    }
+    psAssert (isfinite(*sigma), "invalid model type");
+
+    // psImageSmoothCacheAlloc generates a structure but does not assign the smoothing vector
+    psImageSmooth2dCacheData *smdata = psImageSmooth2dCacheAlloc (nsigma);
+
+    if (modelPSF->type == modelType_GAUSS) {
+	psImageSmooth2dCacheKernel_Gauss (smdata, *sigma);
+    }
+    if (modelPSF->type == modelType_PS1_V1) {
+	psImageSmooth2dCacheKernel_PS1_V1 (smdata, *sigma, *kappa);
+    }
+
+    return smdata;
+}
+
 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
+    modelType_GAUSS = pmModelClassGetType ("PS_MODEL_GAUSS");
+    modelType_PS1_V1 = pmModelClassGetType ("PS_MODEL_PS1_V1");
 
     // count the number of unmasked pixels:
@@ -217,58 +393,10 @@
     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");
-    }
+    constraint->checkLimits = model->class->modelLimits;
+
+    int nParams = pmPCMsetParams (constraint, fitOptions->mode);
 
     if (nPix <  nParams + 1) {
         psTrace ("psModules.objects", 4, "insufficient valid pixels\n");
-	psFree (psf);
 	psFree (constraint);
         model->flags |= PM_MODEL_STATUS_BADARGS;
@@ -278,8 +406,9 @@
     // generate PCM data storage structure
     pmPCMdata *pcm = pmPCMdataAlloc (params, constraint->paramMask, source);
-
-    pcm->psf = psf;
     pcm->modelConv = psMemIncrRefCounter(model);
     pcm->constraint = constraint;
+
+    pcm->poissonErrors = fitOptions->poissonErrors;
+    pcm->nsigma = fitOptions->nsigma;
 
     pcm->nPix = nPix;
@@ -288,21 +417,29 @@
 
 # if (USE_1D_GAUSS)
-    pmModel *modelPSF = source->modelPSF;
-    psAssert (modelPSF, "psf model must be defined");
-    
-    psEllipseAxes axes;
-    bool useReff = pmModelUseReff (modelPSF->type);
-    psF32 *PAR = modelPSF->params->data.F32;
-    pmModelParamsToAxes (&axes, PAR[PM_PAR_SXX], PAR[PM_PAR_SXY], PAR[PM_PAR_SYY], useReff);
-    
-    float FWHM_MAJOR = 2*modelPSF->modelRadius (modelPSF->params, 0.5*PAR[PM_PAR_I0]);
-    float FWHM_MINOR = FWHM_MAJOR * (axes.minor / axes.major);
 
     pcm->use1Dgauss = true;
-    pcm->sigma = 0.5 * (FWHM_MAJOR + FWHM_MINOR) / 2.35;
-    pcm->nsigma = 2.0;
-
-    pcm->smdata = psImageSmooth_PreAlloc_DataAlloc (source->pixels, pcm->sigma, pcm->nsigma);
+    if (USE_1D_CACHE) {
+	pcm->smdata = psImageSmoothCacheSetKernel (&pcm->sigma, &pcm->kappa, pcm->nsigma, source->pixels, source->modelPSF);
+    } else {
+	pcm->smdata2d = psImageSmooth2dCacheSetKernel (&pcm->sigma, &pcm->kappa, pcm->nsigma, source->pixels, source->modelPSF);
+    }
+
 # else
+    // 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
+    pcm->psf = psf;
     pcm->smdata = NULL;
 # endif
@@ -341,53 +478,5 @@
     }
 
-    // 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");
-    }
+    int nParams = pmPCMsetParams (pcm->constraint, fitOptions->mode);
 
     if (pcm->nPix <  nParams + 1) {
@@ -415,6 +504,29 @@
 	    pcm->dmodelsConvFlux->data[n] = psImageCopy (pcm->dmodelsConvFlux->data[n], source->pixels, PS_TYPE_F32);
 	}
-	psFree(pcm->smdata);
-	pcm->smdata = psImageSmooth_PreAlloc_DataAlloc (source->pixels, pcm->sigma, pcm->nsigma);
+
+	// If we have changed the window, we need to redefine the smoothing target vectors (but pcm->sigma,kappa,nsigma remain)
+	if (USE_1D_CACHE) {
+	    psFree(pcm->smdata);
+	    pcm->smdata = psImageSmoothCacheAlloc (source->pixels, pcm->sigma, pcm->nsigma);
+
+	    pmModel *modelPSF = source->modelPSF;
+	    if (modelPSF->type == modelType_GAUSS) {
+		psImageSmoothCacheKernel_Gauss (pcm->smdata, pcm->sigma);
+	    }
+	    if (modelPSF->type == modelType_PS1_V1) {
+		psImageSmoothCacheKernel_PS1_V1 (pcm->smdata, pcm->sigma, pcm->kappa);
+	    }
+	} else {
+	    psFree(pcm->smdata2d);
+	    pcm->smdata2d = psImageSmooth2dCacheAlloc (pcm->nsigma);
+
+	    pmModel *modelPSF = source->modelPSF;
+	    if (modelPSF->type == modelType_GAUSS) {
+		// psImageSmooth2dCacheKernel_Gauss (pcm->smdata2d, pcm->sigma);
+	    }
+	    if (modelPSF->type == modelType_PS1_V1) {
+		psImageSmooth2dCacheKernel_PS1_V1 (pcm->smdata2d, pcm->sigma, pcm->kappa);
+	    }
+	}
     }
 
@@ -423,5 +535,5 @@
 
 // construct a realization of the source model
-bool pmPCMCacheModel (pmSource *source, psImageMaskType maskVal, int psfSize) {
+bool pmPCMCacheModel (pmSource *source, psImageMaskType maskVal, int psfSize, float nsigma) {
 
     PS_ASSERT_PTR_NON_NULL(source, false);
@@ -440,24 +552,18 @@
     // convolve the model image with the PSF
     if (USE_1D_GAUSS) {
-	// do not use the threaded, mask-aware version of this code (psImageSmoothMaskPixelsThread):
-	// * the model flux is not masked
-	// * threading takes place above this level
 	
-	// define the Gauss parameters from the psf
-	pmModel *modelPSF = source->modelPSF;
-	psAssert (modelPSF, "psf model must be defined");
-    
-	psEllipseAxes axes;
-	bool useReff = pmModelUseReff (modelPSF->type);
-	psF32 *PAR = modelPSF->params->data.F32;
-	pmModelParamsToAxes (&axes, PAR[PM_PAR_SXX], PAR[PM_PAR_SXY], PAR[PM_PAR_SYY], useReff);
-    
-	float FWHM_MAJOR = 2*modelPSF->modelRadius (modelPSF->params, 0.5*PAR[PM_PAR_I0]);
-	float FWHM_MINOR = FWHM_MAJOR * (axes.minor / axes.major);
-
-	float sigma = 0.5 * (FWHM_MAJOR + FWHM_MINOR) / 2.35;
-	float nsigma = 2.0;
-
-	psImageSmooth (source->modelFlux, sigma, nsigma);
+	float sigma = NAN;
+	float kappa = NAN;
+
+	if (USE_1D_CACHE) {
+	    psImageSmoothCacheData *smdata = psImageSmoothCacheSetKernel (&sigma, &kappa, nsigma, source->modelFlux, source->modelPSF);
+	    psImageSmoothCache_F32 (source->modelFlux, smdata);
+	    psFree (smdata);
+	} else {
+	    psImageSmooth2dCacheData *smdata = psImageSmooth2dCacheSetKernel (&sigma, &kappa, nsigma, source->modelFlux, source->modelPSF);
+	    psImageSmooth2dCache_F32 (source->modelFlux, smdata);
+	    psFree (smdata);
+	}
+	// old call: psImageSmooth (source->modelFlux, sigma, nsigma);
     } else {
 	// make sure we save a cached copy of the psf flux
@@ -478,2 +584,48 @@
 }
 
+// construct a realization of the source model
+bool pmPCMMakeModel (pmSource *source, pmModel *model, float Nsigma, psImageMaskType maskVal, int psfSize) {
+
+    PS_ASSERT_PTR_NON_NULL(source, false);
+
+    // if we already have a cached image, re-use that memory
+    source->modelFlux = psImageCopy (source->modelFlux, source->pixels, PS_TYPE_F32);
+    psImageInit (source->modelFlux, 0.0);
+
+    // modelFlux always has unity normalization (I0 = 1.0)
+    // pmModelAdd (source->modelFlux, source->maskObj, model, PM_MODEL_OP_FULL | PM_MODEL_OP_NORM, maskVal);
+    pmModelAdd (source->modelFlux, NULL, model, PM_MODEL_OP_FULL | PM_MODEL_OP_SKY | PM_MODEL_OP_NORM, maskVal);
+
+    // convolve the model image with the PSF
+    if (USE_1D_GAUSS) {
+
+	float sigma = NAN;
+	float kappa = NAN;
+
+	if (USE_1D_CACHE) {
+	    psImageSmoothCacheData *smdata = psImageSmoothCacheSetKernel (&sigma, &kappa, Nsigma, source->modelFlux, source->modelPSF);
+	    psImageSmoothCache_F32 (source->modelFlux, smdata);
+	    psFree (smdata);
+	} else {
+	    psImageSmooth2dCacheData *smdata = psImageSmooth2dCacheSetKernel (&sigma, &kappa, Nsigma, source->modelFlux, source->modelPSF);
+	    psImageSmooth2dCache_F32 (source->modelFlux, smdata);
+	    psFree (smdata);
+	}
+	// old call: psImageSmooth (source->modelFlux, sigma, nsigma);
+    } else {
+	// 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;
+	}
+
+	// XXX not sure if I can place the output on top of the input
+	psImageConvolveFFT (source->modelFlux, source->modelFlux, NULL, 0, psf);
+    }
+    return true;
+}
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPCMdata.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPCMdata.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPCMdata.h	(revision 37066)
@@ -14,4 +14,8 @@
 /// @addtogroup Objects Object Detection / Analysis Functions
 /// @{
+
+// XXX this is basically for testing -- when I am happy with the convolution process, I'll strip this out
+# define USE_1D_CACHE 0
+# define USE_1D_GAUSS 1
 
 /** pmPCMdata : PSF Convolved Model data storage structure
@@ -36,9 +40,14 @@
     int nDOF;
 
+    bool poissonErrors;
+
     bool use1Dgauss;
+    float kappa;
     float sigma;
     float nsigma;
 
-    psImageSmooth_PreAlloc_Data *smdata;
+    // psArray *smdata;
+    psImageSmoothCacheData *smdata;
+    psImageSmooth2dCacheData *smdata2d;
 } pmPCMdata;
 
@@ -96,5 +105,7 @@
 bool pmSourceFitPCM (pmPCMdata *pcm, pmSource *source, pmSourceFitOptions *fitOptions, psImageMaskType maskVal, psImageMaskType markVal, int psfSize);
 
-bool pmPCMCacheModel (pmSource *source, psImageMaskType maskVal, int psfSize);
+bool pmPCMCacheModel (pmSource *source, psImageMaskType maskVal, int psfSize, float nsigma);
+
+bool pmPCMMakeModel (pmSource *source, pmModel *model, float Nsigma, psImageMaskType maskVal, int psfSize);
 
 /// @}
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPSF.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPSF.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPSF.c	(revision 37066)
@@ -37,11 +37,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -328,5 +329,5 @@
 // convert the parameters used in the fitted source model to the psEllipseAxes representation
 // (major,minor,theta)
-psEllipseAxes pmPSF_ModelToAxes (psF32 *modelPar, pmModelType type)
+psEllipseAxes pmPSF_ModelToAxes (psF32 *modelPar, bool useReff)
 {
     psEllipseAxes axes;
@@ -337,5 +338,4 @@
     PS_ASSERT_PTR_NON_NULL(modelPar, axes);
 
-    bool useReff = pmModelUseReff (type);
     pmModelParamsToAxes (&axes, modelPar[PM_PAR_SXX], modelPar[PM_PAR_SXY], modelPar[PM_PAR_SYY], useReff);
     return axes;
@@ -344,5 +344,5 @@
 // convert the psEllipseAxes representation (major,minor,theta) to the parameters used in the
 // fitted source model
-bool pmPSF_AxesToModel (psF32 *modelPar, psEllipseAxes axes, pmModelType type)
+bool pmPSF_AxesToModel (psF32 *modelPar, psEllipseAxes axes, bool useReff)
 {
     PS_ASSERT_PTR_NON_NULL(modelPar, false);
@@ -356,5 +356,4 @@
     }
     
-    bool useReff = pmModelUseReff (type);
     pmModelAxesToParams (&modelPar[PM_PAR_SXX], &modelPar[PM_PAR_SXY], &modelPar[PM_PAR_SYY], axes, useReff);
     return true;
@@ -419,9 +418,9 @@
 
     // get the model full-width at half-max
-    float fwhmMajor = 2*model->modelRadius (model->params, 0.5);
+    float fwhmMajor = 2*model->class->modelRadius (model->params, 0.5);
 
 # if (0)
     psF32 *params = model->params->data.F32; // Model parameters
-    psEllipseAxes axes = pmPSF_ModelToAxes(params, MAX_AXIS_RATIO, model->type); // Ellipse axes
+    psEllipseAxes axes = pmPSF_ModelToAxes(params, MAX_AXIS_RATIO, model->class->useReff); // Ellipse axes
 
     // Curiously, the minor axis can be larger than the major axis, so need to check.
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPSF.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPSF.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPSF.h	(revision 37066)
@@ -106,9 +106,9 @@
 pmPSF *pmPSFBuildSimple (char *typeName, float sxx, float syy, float sxy, ...);
 
-bool pmPSF_AxesToModel (psF32 *modelPar, psEllipseAxes axes, pmModelType type);
+bool pmPSF_AxesToModel (psF32 *modelPar, psEllipseAxes axes, bool useReff);
 bool pmPSF_FitToModel (psF32 *fittedPar, float minMinorAxis, bool useReff);
 
 psEllipsePol pmPSF_ModelToFit (psF32 *modelPar, bool useReff);
-psEllipseAxes pmPSF_ModelToAxes (psF32 *modelPar, pmModelType type);
+psEllipseAxes pmPSF_ModelToAxes (psF32 *modelPar, bool useReff);
 
 /// Calculate FWHM value from a PSF
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPSF_IO.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPSF_IO.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPSF_IO.c	(revision 37066)
@@ -47,11 +47,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -510,11 +511,13 @@
         psMetadataAddF32 (header, PS_LIST_TAIL, "SKY_BIAS", PS_DATA_F32, "sky bias level", psf->skyBias);
 
-	float PSF_APERTURE =  psMetadataLookupF32(&status, roAnalysis, "PSF_APERTURE");
-	if (status) {
+	if (roAnalysis) {
+	  float PSF_APERTURE =  psMetadataLookupF32(&status, roAnalysis, "PSF_APERTURE");
+	  if (status) {
 	    psMetadataAddF32 (header, PS_LIST_TAIL, "PSF_APERTURE", PS_DATA_F32, "aperture for psf objects", PSF_APERTURE);
-	}
-	float PSF_FIT_RADIUS =  psMetadataLookupF32(&status, roAnalysis, "PSF_FIT_RADIUS");
-	if (status) {
+	  }
+	  float PSF_FIT_RADIUS =  psMetadataLookupF32(&status, roAnalysis, "PSF_FIT_RADIUS");
+	  if (status) {
 	    psMetadataAddF32 (header, PS_LIST_TAIL, "PSF_FIT_RADIUS", PS_DATA_F32, "aperture for psf objects", PSF_FIT_RADIUS);
+	  }
 	}
 
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPSFtry.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPSFtry.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPSFtry.c	(revision 37066)
@@ -29,11 +29,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPSFtryFitEXT.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPSFtryFitEXT.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPSFtryFitEXT.c	(revision 37066)
@@ -29,11 +29,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceUtils.h"
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPSFtryFitPSF.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPSFtryFitPSF.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPSFtryFitPSF.c	(revision 37066)
@@ -27,11 +27,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -118,38 +119,46 @@
     psfTry->psf->nPSFstars = Npsf;
 
-    // DEBUG code: save the PSF model fit data in detail
-# ifdef DEBUG
-
-    char filename[64];
-    snprintf (filename, 64, "psffit.%dx%d.dat", psfTry->psf->trendNx, psfTry->psf->trendNy);
-    FILE *f = fopen (filename, "w");
-    psAssert (f, "failed open");
-
-    for (int i = 0; i < psfTry->sources->n; i++) {
-
-	// skip masked sources
-        if (psfTry->mask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PSFTRY_MASK_ALL) continue;
-
-        pmSource *source = psfTry->sources->data[i];
-
-	fprintf (f, "%6.1f %6.1f : %6.1f %6.1f : %8.3f %8.3f %8.3f : %f : %f %f %f : %f\n",
-		 source->peak->xf, source->peak->yf, 
-		 source->modelPSF->params->data.F32[PM_PAR_XPOS], source->modelPSF->params->data.F32[PM_PAR_YPOS], 
-		 source->psfMag, source->apMag, source->psfMagErr,
-		 source->modelPSF->params->data.F32[PM_PAR_I0], 
-		 source->modelPSF->params->data.F32[PM_PAR_SXX], source->modelPSF->params->data.F32[PM_PAR_SXY], 
-		 source->modelPSF->params->data.F32[PM_PAR_SYY], source->modelPSF->params->data.F32[PM_PAR_7]);
-    }
-    fclose (f);
-# endif
-
     pmSourceVisualShowModelFits (psfTry->psf, psfTry->sources, maskVal);
 
-    psLogMsg ("psphot.psftry", PS_LOG_MINUTIA, "fit psf:   %f sec for %d of %ld sources\n", psTimerMark ("psf.fit"), Npsf, psfTry->sources->n);
+    psLogMsg ("psphot.psftry", PS_LOG_MINUTIA, "fit psf:   %f sec for %d of %ld sources (%d x %d model)\n", psTimerMark ("psf.fit"), Npsf, psfTry->sources->n, psfTry->psf->trendNx, psfTry->psf->trendNy);
     psTrace ("psModules.object", 3, "keeping %d of %ld PSF candidates (PSF)\n", Npsf, psfTry->sources->n);
 
     if (Npsf == 0) {
-        psError(PS_ERR_UNKNOWN, false, "No sources with good PSF fits after model is built.");
-        return false;
+#if 0
+	// DEBUG code: save the PSF model fit data in detail
+
+	char hostname[256];
+	gethostname (hostname, 256);
+
+	int pid = getpid();
+
+	char filename[64];
+	snprintf (filename, 64, "psffit.%s.%d.%dx%d.dat", hostname, pid, psfTry->psf->trendNx, psfTry->psf->trendNy);
+	FILE *f = fopen (filename, "w");
+	psAssert (f, "failed open");
+
+	for (int i = 0; i < psfTry->sources->n; i++) {
+
+	    // skip masked sources
+	  // if (psfTry->mask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PSFTRY_MASK_ALL) continue;
+
+	    pmSource *source = psfTry->sources->data[i];
+
+            if (!source->modelPSF) continue;
+
+	    float par7 = (source->modelPSF->params->n == 7) ? -100 : source->modelPSF->params->data.F32[PM_PAR_7];
+	    fprintf (f, "%6.1f %6.1f : %6.1f %6.1f : %8.3f %8.3f %8.3f : %f : %f %f %f : %f %d\n",
+		     source->peak->xf, source->peak->yf, 
+		     source->modelPSF->params->data.F32[PM_PAR_XPOS], source->modelPSF->params->data.F32[PM_PAR_YPOS], 
+		     source->psfMag, source->apMag, source->psfMagErr,
+		     source->modelPSF->params->data.F32[PM_PAR_I0], 
+		     source->modelPSF->params->data.F32[PM_PAR_SXX], source->modelPSF->params->data.F32[PM_PAR_SXY], 
+		     source->modelPSF->params->data.F32[PM_PAR_SYY], par7,
+		     psfTry->mask->data.PS_TYPE_VECTOR_MASK_DATA[i]);
+	}
+	fclose (f);
+#endif
+	psError(PS_ERR_UNKNOWN, false, "No sources with good PSF fits after model is built.");
+	return false;
     }
 
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPSFtryMakePSF.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPSFtryMakePSF.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPSFtryMakePSF.c	(revision 37066)
@@ -28,11 +28,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -211,5 +212,5 @@
         assert (source->modelEXT); // all unmasked sources should have modelEXT
 
-	bool useReff = pmModelUseReff (source->modelEXT->type);
+	bool useReff = source->modelEXT->class->useReff;
         psEllipsePol pol = pmPSF_ModelToFit (source->modelEXT->params->data.F32, useReff);
 
@@ -217,4 +218,26 @@
         e1->data.F32[i] = pol.e1;
         e2->data.F32[i] = pol.e2;
+    }
+
+    // weed out extreme e0 outliers here: find the median and exclude points not in the
+    // range MEDIAN / 5 < e0 < 5 * MEDIAN
+    { 
+      psStats *e0stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
+      if (psVectorStats (e0stats, e0, NULL, srcMask, 0xff)) {
+	float e0med = e0stats->sampleMedian;
+    
+	for (int i = 0; i < sources->n; i++) {
+	  // skip any masked sources (failed to fit one of the model steps or get a magnitude)
+	  if (srcMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) continue;
+
+	  if (e0->data.F32[i] < 0.2*e0med) {
+	    srcMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = PSFTRY_MASK_OUTLIER;
+	  }
+	  if (e0->data.F32[i] > 5.0*e0med) {
+	    srcMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = PSFTRY_MASK_OUTLIER;
+	  }
+	}
+      }
+      psFree (e0stats);
     }
 
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPSFtryMetric.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPSFtryMetric.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPSFtryMetric.c	(revision 37066)
@@ -28,11 +28,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPSFtryModel.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPSFtryModel.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPSFtryModel.c	(revision 37066)
@@ -29,11 +29,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -111,10 +112,19 @@
 
     // set the max order (0 = constant) which the number of psf stars can support:
+    int MaxOrderForStars = 0;
+
+    // we require only 3 stars for n = 0, increase stars / cell for higher order
+    if (sources->n >=  16) MaxOrderForStars = 1; //  4 cells, 4 per cell
+    if (sources->n >=  54) MaxOrderForStars = 2; //  9 cells, 6 per cell
+    if (sources->n >= 128) MaxOrderForStars = 3; // 16 cells, 8 per cell
+    if (sources->n >= 300) MaxOrderForStars = 4; // 25 cells, 12 per cell
+    if (sources->n >  576) MaxOrderForStars = 5; // 36 cells, 16 per cell
+
     // rule of thumb: require 3 stars per 'cell' (order+1)^2
-    int MaxOrderForStars = 0;
-    if (sources->n >= 12) MaxOrderForStars = 1; // 4 cells
-    if (sources->n >= 27) MaxOrderForStars = 2; // 9 cells
-    if (sources->n >= 48) MaxOrderForStars = 3; // 16 cells
-    if (sources->n >  75) MaxOrderForStars = 4; // 25 cells
+    // if (sources->n >= 12) MaxOrderForStars = 1; // 4 cells
+    // if (sources->n >= 27) MaxOrderForStars = 2; // 9 cells
+    // if (sources->n >= 48) MaxOrderForStars = 3; // 16 cells
+    // if (sources->n >= 75) MaxOrderForStars = 4; // 25 cells
+    // if (sources->n > 108) MaxOrderForStars = 5; // 36 cells
 
     int orderMax = PS_MAX (options->psfTrendNx, options->psfTrendNy);
@@ -236,4 +246,14 @@
     // linear clipped fit of chisq trend vs flux
     if (options->chiFluxTrend) {
+
+	if (0) {
+	    FILE *f = fopen ("test.psf.dat", "w");
+	    int fd = fileno (f);
+	    p_psVectorPrint (fd, flux, "flux");
+	    p_psVectorPrint (fd, chisq, "chisq");
+	    p_psVectorPrint (fd, mask, "mask");
+	    fclose (f);
+	}
+
         bool result = psVectorClipFitPolynomial1D(psfTry->psf->ChiTrend, options->stats,
                                                   mask, 0xff, chisq, NULL, flux);
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPhotObj.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPhotObj.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmPhotObj.c	(revision 37066)
@@ -29,11 +29,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSource.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSource.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSource.c	(revision 37066)
@@ -33,7 +33,7 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
@@ -41,4 +41,5 @@
 #include "pmSourcePhotometry.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 
@@ -66,5 +67,8 @@
     psFree(tmp->extpars);
     psFree(tmp->diffStats);
+    psFree(tmp->galaxyFits);
     psFree(tmp->radialAper);
+    psFree(tmp->lensingOBJ);
+    psFree(tmp->lensingPSF);
     psTrace("psModules.objects", 10, "---- end ----\n");
 }
@@ -142,4 +146,5 @@
     source->apMagRaw  	     = NAN;
     source->apRadius  	     = NAN;
+    source->apNpixels  	     = 0;
     source->apFlux    	     = NAN;
     source->apFluxErr 	     = NAN; 
@@ -158,4 +163,5 @@
     source->sky    	     = NAN;
     source->skyErr 	     = NAN;    
+    source->extSN  	     = NAN;    
 
     source->region = psRegionSet(NAN, NAN, NAN, NAN);
@@ -164,4 +170,7 @@
     source->extpars = NULL;
     source->diffStats = NULL;
+    source->galaxyFits = NULL;
+    source->lensingOBJ = NULL;
+    source->lensingPSF = NULL;
     source->radialAper = NULL;
     source->parent = NULL;
@@ -229,4 +238,5 @@
     source->apMagRaw  	     = in->apMagRaw;
     source->apRadius  	     = in->apRadius;
+    source->apNpixels  	     = in->apNpixels;
     source->apFlux    	     = in->apFlux;
     source->apFluxErr 	     = in->apFluxErr;
@@ -247,4 +257,6 @@
 
     source->region           = in->region;
+
+    // XXX I am not copying the pointers to things like the blends, satstar profile, galaxyFits, etc
 
     return(source);
@@ -690,5 +702,4 @@
             // why do we recalculate moments here?
 	    // we already attempt to do this in psphotSourceStats
-            // pmSourceMoments (source, INNER_RADIUS);
             Nsatstar ++;
             continue;
@@ -804,191 +815,4 @@
     return true;
 }
-
-/******************************************************************************
-pmSourceMoments(source, radius): this function takes a subImage defined in the
-pmSource data structure, along with the peak location, and determines the
-various moments associated with that peak.
-
-Requires the following to have been created:
-    pmSource
-    pmSource->peak
-    pmSource->pixels
-    pmSource->variance
-    pmSource->mask
-
-XXX: The peak calculations are done in image coords, not subImage coords.
-
-XXX EAM : this version clips input pixels on S/N
-XXX EAM : this version returns false for several reasons
-*****************************************************************************/
-# define VALID_RADIUS(X,Y,RAD2) (((RAD2) >= (PS_SQR(X) + PS_SQR(Y))) ? 1 : 0)
-
-/*** this been moved to pmSourceMoments.c ***/
-# if (0)
-bool pmSourceMoments(pmSource *source,
-                     psF32 radius)
-{
-    psTrace("psModules.objects", 10, "---- begin ----\n");
-    PS_ASSERT_PTR_NON_NULL(source, false);
-    PS_ASSERT_PTR_NON_NULL(source->peak, false);
-    PS_ASSERT_PTR_NON_NULL(source->pixels, false);
-    PS_ASSERT_FLOAT_LARGER_THAN(radius, 0.0, false);
-
-    //
-    // XXX: Verify the setting for sky if source->moments == NULL.
-    //
-    psF32 sky = 0.0;
-    if (source->moments == NULL) {
-        source->moments = pmMomentsAlloc();
-    } else {
-        sky = source->moments->Sky;
-    }
-
-    //
-    // Sum = SUM (z - sky)
-    // X1  = SUM (x - xc)*(z - sky)
-    // X2  = SUM (x - xc)^2 * (z - sky)
-    // XY  = SUM (x - xc)*(y - yc)*(z - sky)
-    //
-    psF32 peakPixel = -PS_MAX_F32;
-    psS32 numPixels = 0;
-    psF32 Sum = 0.0;
-    psF32 Var = 0.0;
-    psF32 X1 = 0.0;
-    psF32 Y1 = 0.0;
-    psF32 X2 = 0.0;
-    psF32 Y2 = 0.0;
-    psF32 XY = 0.0;
-    psF32 x  = 0;
-    psF32 y  = 0;
-    psF32 R2 = PS_SQR(radius);
-
-    psF32 xPeak = source->peak->x;
-    psF32 yPeak = source->peak->y;
-    psF32 xOff = source->pixels->col0 - source->peak->x;
-    psF32 yOff = source->pixels->row0 - source->peak->y;
-
-    // XXX why do I get different results for these two methods of finding Sx?
-    // XXX Sx, Sy would be better measured if we clip pixels close to sky
-    // XXX Sx, Sy can still be imaginary, so we probably need to keep Sx^2?
-    // We loop through all pixels in this subimage (source->pixels), and for each
-    // pixel that is not masked, AND within the radius of the peak pixel, we
-    // proceed with the moments calculation.  need to do two loops for a
-    // numerically stable result.  first loop: get the sums.
-    // XXX EAM : mask == 0 is valid
-
-    for (psS32 row = 0; row < source->pixels->numRows ; row++) {
-
-        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) {
-                    vMsk++;
-                    psTrace("psModules.objects", 10, "Ignoring pixel %d,%d due to mask: %d\n",
-                            col, row, (int)*vMsk);
-                    continue;
-                }
-                vMsk++;
-            }
-            if (isnan(*vPix)) continue;
-
-            psF32 xDiff = col + xOff;
-            psF32 yDiff = row + yOff;
-
-            // radius is just a function of (xDiff, yDiff)
-            if (!VALID_RADIUS(xDiff, yDiff, R2)) {
-#if 1
-                psTrace("psModules.objects", 10, "Ignoring pixel %d,%d due to position: %f %f\n",
-                        col, row, xDiff, yDiff);
-#endif
-                continue;
-            }
-
-            psF32 pDiff = *vPix - sky;
-            psF32 wDiff = *vWgt;
-
-            // XXX EAM : check for valid S/N in pixel
-            // XXX EAM : should this limit be user-defined?
-#if 1
-            if (PS_SQR(pDiff) < wDiff) {
-                psTrace("psModules.objects", 10, "Ignoring pixel %d,%d due to insignificance: %f, %f\n",
-                        col, row, pDiff, wDiff);
-                continue;
-            }
-#endif
-
-            Var += wDiff;
-            Sum += pDiff;
-
-            psF32 xWght = xDiff * pDiff;
-            psF32 yWght = yDiff * pDiff;
-
-            X1  += xWght;
-            Y1  += yWght;
-
-            XY  += xDiff * yWght;
-            X2  += xDiff * xWght;
-            Y2  += yDiff * yWght;
-
-            peakPixel = PS_MAX (*vPix, peakPixel);
-            numPixels++;
-        }
-    }
-
-    // if we have less than (1/4) of the possible pixels, force a retry
-    // XXX EAM - the limit is a bit arbitrary.  make it user defined?
-    if ((numPixels < 0.75*R2) || (Sum <= 0)) {
-        psTrace ("psModules.objects", 3, "insufficient valid pixels (%d vs %d; %f) for source\n",
-                 numPixels, (int)(0.75*R2), Sum);
-        psTrace("psModules.objects", 10, "---- end (false) ----\n");
-        return (false);
-    }
-
-    psTrace ("psModules.objects", 4, "sky: %f  Sum: %f  X1: %f  Y1: %f  X2: %f  Y2: %f  XY: %f  Npix: %d\n",
-             sky, Sum, X1, Y1, X2, Y2, XY, numPixels);
-
-    //
-    // first moment X  = X1/Sum + xc
-    // second moment X = sqrt (X2/Sum - (X1/Sum)^2)
-    // Sxy             = XY / Sum
-    //
-    x = X1/Sum;
-    y = Y1/Sum;
-    if ((fabs(x) > radius) || (fabs(y) > radius)) {
-        psTrace ("psModules.objects", 3, "large centroid swing; invalid peak %d, %d\n",
-                 source->peak->x, source->peak->y);
-        psTrace("psModules.objects", 10, "---- end(false)  ----\n");
-        return (false);
-    }
-
-    source->moments->Mx = x + xPeak;
-    source->moments->My = y + yPeak;
-
-    // XXX EAM : Sxy needs to have x*y subtracted
-    source->moments->Mxy = XY/Sum - x*y;
-    source->moments->Sum = Sum;
-    source->moments->SN  = Sum / sqrt(Var);
-    source->moments->Peak = peakPixel;
-    source->moments->nPixels = numPixels;
-
-    // XXX EAM : these values can be negative, so we need to limit the range
-    // XXX EAM : make the use of this consistent: should this be the second moment or sqrt?
-    // source->moments->Mxx = sqrt(PS_MAX(X2/Sum - PS_SQR(x), 0));
-    // source->moments->Myy = sqrt(PS_MAX(Y2/Sum - PS_SQR(y), 0));
-    source->moments->Mxx = PS_MAX(X2/Sum - PS_SQR(x), 0);
-    source->moments->Myy = PS_MAX(Y2/Sum - PS_SQR(y), 0);
-
-    psTrace ("psModules.objects", 4,
-             "sky: %f  Sum: %f  Mx: %f  My: %f  Mxx: %f  Myy: %f  Mxy: %f\n",
-             sky, Sum, source->moments->Mx, source->moments->My,
-             source->moments->Mxx, source->moments->Myy, source->moments->Mxy);
-
-    psTrace("psModules.objects", 10, "---- end ----\n");
-    return(true);
-}
-# endif
 
 // construct a realization of the source model
@@ -1165,5 +989,5 @@
     if (!isfinite(oldI0)) return false;
 
-    bool useReff = pmModelUseReff (model->type);
+    bool useReff = model->class->useReff;
     pmModelParamsToAxes (&axes, PAR[PM_PAR_SXX], PAR[PM_PAR_SXY], PAR[PM_PAR_SYY], useReff);
 
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSource.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSource.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSource.h	(revision 37066)
@@ -40,4 +40,6 @@
     PM_SOURCE_TMPF_PETRO_KEEP        = 0x0100,
     PM_SOURCE_TMPF_PETRO_SKIP        = 0x0200,
+    PM_SOURCE_TMPF_EXT_FIT           = 0x0400,  // not just galaxies (trails as well)
+    PM_SOURCE_TMPF_PETRO             = 0x0800,
 } pmSourceTmpF;
 
@@ -95,4 +97,5 @@
     float apMagRaw;                     ///< raw mag in given aperture
     float apRadius;			///< radius for aperture magnitude
+    int   apNpixels;			///< number of unmasked pixels in aperture
     float apFlux;                       ///< apFlux corresponding to psfMag or extMag (depending on type)
     float apFluxErr;                    ///< apFluxErr corresponding to psfMag or extMag (depending on type)
@@ -111,4 +114,5 @@
     float sky;				///< The sky at the center of the object 
     float skyErr;			///< The sky error at the center of the object
+    float extSN;                        ///< for externally supplied source the kron signal to noise (used by full force)
 
     psRegion region;                    ///< area on image covered by selected pixels
@@ -117,4 +121,7 @@
     pmSourceExtendedPars *extpars;      ///< extended source parameters
     pmSourceDiffStats *diffStats;       ///< extra parameters for difference detections
+    psArray *galaxyFits;                ///< fits to galaxy models (psphotFullForce only)
+    pmSourceLensing *lensingOBJ;        ///< lensing moments parameters (per object)
+    pmSourceLensing *lensingPSF;        ///< lensing moments parameters (psf, interpolated)
     psArray *radialAper;		///< radial flux in circular apertures
     pmSource *parent;			///< reference to the master source from which this is derived
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceContour.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceContour.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceContour.c	(revision 37066)
@@ -33,11 +33,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceExtendedPars.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceExtendedPars.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceExtendedPars.c	(revision 37066)
@@ -286,2 +286,25 @@
     return pars;
 }
+
+// *** pmSourceExtFitPars describes extra metadata related to an extended fit
+static void pmSourceGalaxyFitsFree (pmSourceGalaxyFits *tmp) {
+  
+    psFree (tmp->Flux);
+    psFree (tmp->dFlux);
+    psFree (tmp->chisq);
+
+    return;
+}
+
+pmSourceGalaxyFits *pmSourceGalaxyFitsAlloc (void) {
+
+    pmSourceGalaxyFits *tmp = (pmSourceGalaxyFits *) psAlloc(sizeof(pmSourceGalaxyFits));
+    psMemSetDeallocator(tmp, (psFreeFunc) pmSourceGalaxyFitsFree);
+
+    tmp->Flux  = psVectorAllocEmpty (25, PS_TYPE_F32);
+    tmp->dFlux = psVectorAllocEmpty (25, PS_TYPE_F32);
+    tmp->chisq = psVectorAllocEmpty (25, PS_TYPE_F32);
+    tmp->nPix = 0;
+
+    return tmp;
+}
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceExtendedPars.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceExtendedPars.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceExtendedPars.h	(revision 37066)
@@ -82,4 +82,19 @@
 } pmSourceExtFitPars;
 
+typedef struct {
+  int       modelType;
+  psVector *Flux;
+  psVector *dFlux;
+  psVector *chisq;
+  int       nPix;
+  bool      reducedTrials;
+  float     fRmajorMin;
+  float     fRmajorMax;
+  float     fRmajorDel;
+  float     fRminorMin;
+  float     fRminorMax;
+  float     fRminorDel;
+} pmSourceGalaxyFits;
+
 pmSourceRadialFlux *pmSourceRadialFluxAlloc();
 bool psMemCheckSourceRadialFlux(psPtr ptr);
@@ -109,4 +124,6 @@
 pmSourceExtFitPars *pmSourceExtFitParsAlloc (void);
 
+pmSourceGalaxyFits *pmSourceGalaxyFitsAlloc (void);
+
 /// @}
 # endif /* PM_SOURCE_H */
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceFitModel.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceFitModel.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceFitModel.c	(revision 37066)
@@ -33,11 +33,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourcePhotometry.h"
@@ -59,4 +60,5 @@
     opt->maxTol = 1.00;
     opt->weight = 1.00;
+    opt->nsigma = 5.00;
     opt->maxChisqDOF = NAN;
     opt->poissonErrors = true;
@@ -66,4 +68,5 @@
     opt->gainFactorMode = 0;
     opt->chisqConvergence = true;
+    opt->isInteractive = false;
 
     return opt;
@@ -167,5 +170,5 @@
     psMinConstraint *constraint = psMinConstraintAlloc();
     constraint->paramMask = psVectorAlloc (params->n, PS_TYPE_VECTOR_MASK);
-    constraint->checkLimits = model->modelLimits;
+    constraint->checkLimits = model->class->modelLimits;
 
     // set parameter mask based on fitting mode
@@ -230,6 +233,6 @@
     // 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->class->modelLimits (PS_MINIMIZE_PARAM_MIN, i, params->data.F32, NULL);
+	model->class->modelLimits (PS_MINIMIZE_PARAM_MAX, i, params->data.F32, NULL);
     }
 
@@ -247,8 +250,9 @@
     myMin->gainFactorMode = options->gainFactorMode;
     myMin->chisqConvergence = options->chisqConvergence;
+    myMin->isInteractive = options->isInteractive;
 
     psImage *covar = psImageAlloc (params->n, params->n, PS_TYPE_F32);
 
-    fitStatus = psMinimizeLMChi2(myMin, covar, params, constraint, x, y, yErr, model->modelFunc);
+    fitStatus = psMinimizeLMChi2(myMin, covar, params, constraint, x, y, yErr, model->class->modelFunc);
     for (int i = 0; i < dparams->n; i++) {
         if ((constraint->paramMask != NULL) && constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[i])
@@ -279,5 +283,11 @@
     // set the model success or failure status
     model->flags |= PM_MODEL_STATUS_FITTED;
-    if (!fitStatus) model->flags |= PM_MODEL_STATUS_NONCONVERGE;
+    if (!fitStatus) {
+	if (isnan(myMin->value)) {
+	  model->flags |= PM_MODEL_STATUS_NAN_CHISQ;
+	} else {
+	  model->flags |= PM_MODEL_STATUS_NONCONVERGE;
+	}
+    }
 
     if (myMin->chisqConvergence) {
@@ -296,5 +306,5 @@
             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, model->modelFunc);
+        psMinimizeGaussNewtonDelta(delta, params, altmask, x, y, yErr, model->class->modelFunc);
 
         for (int i = 0; i < dparams->n; i++) {
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceFitModel.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceFitModel.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceFitModel.h	(revision 37066)
@@ -21,4 +21,5 @@
     PM_SOURCE_FIT_EXT_AND_SKY,
     PM_SOURCE_FIT_INDEX,
+    PM_SOURCE_FIT_SHAPE,
     PM_SOURCE_FIT_NO_INDEX,
     PM_SOURCE_FIT_TRAIL,
@@ -33,8 +34,10 @@
     float weight;			///< use this weight for constant-weight fits
     float covarFactor;			///< covariance factor for calculating the chisq
+    float nsigma;                       ///< how far out to convolve
     bool poissonErrors;			///< use poisson errors for fits?
     bool saveCovariance;
     int gainFactorMode;
     bool chisqConvergence; 
+    bool isInteractive;
 } pmSourceFitOptions;
 
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceFitPCM.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceFitPCM.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceFitPCM.c	(revision 37066)
@@ -31,11 +31,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourcePhotometry.h"
@@ -51,4 +52,6 @@
 # define TIMING 0
 
+bool pmSourceChisqModelFlux (pmSource *source, pmModel *model, psImageMaskType maskVal);
+
 bool pmSourceFitPCM (pmPCMdata *pcm, pmSource *source, pmSourceFitOptions *fitOptions, psImageMaskType maskVal, psImageMaskType markVal, int psfSize) {
     
@@ -60,6 +63,6 @@
     // 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);
+	pcm->modelConv->class->modelLimits (PS_MINIMIZE_PARAM_MIN, i, params->data.F32, NULL);
+	pcm->modelConv->class->modelLimits (PS_MINIMIZE_PARAM_MAX, i, params->data.F32, NULL);
     }
 
@@ -68,4 +71,5 @@
     myMin->chisqConvergence = fitOptions->chisqConvergence;
     myMin->gainFactorMode = fitOptions->gainFactorMode;
+    myMin->isInteractive = fitOptions->isInteractive;
 
     psImage *covar = psImageAlloc (params->n, params->n, PS_TYPE_F32);
@@ -112,5 +116,6 @@
     } else {
 	// xxx this is wrong because it does not convolve with the psf
-	pmSourceChisqUnsubtracted (source, pcm->modelConv, maskVal);
+	pmPCMMakeModel (source, pcm->modelConv, pcm->nsigma, maskVal, psfSize);
+	pmSourceChisqModelFlux (source, pcm->modelConv, maskVal);
     }
     if (TIMING) { t4 = psTimerMark ("pmSourceFitPCM"); }
@@ -118,5 +123,12 @@
     // set the model success or failure status
     pcm->modelConv->flags |= PM_MODEL_STATUS_FITTED;
-    if (!fitStatus) pcm->modelConv->flags |= PM_MODEL_STATUS_NONCONVERGE;
+
+    if (!fitStatus) {
+	if (isnan(myMin->value)) {
+	    pcm->modelConv->flags |= PM_MODEL_STATUS_NAN_CHISQ;
+	} else {
+	    pcm->modelConv->flags |= PM_MODEL_STATUS_NONCONVERGE;
+	}
+    }
 
     if (myMin->chisqConvergence) {
@@ -153,5 +165,5 @@
 bool pmSourceModelGuessPCM (pmPCMdata *pcm, pmSource *source, psImageMaskType maskVal, psImageMaskType markVal) {
 
-    if (!pcm->modelConv->modelGuess(pcm->modelConv, source, maskVal, markVal)) {
+    if (!pcm->modelConv->class->modelGuess(pcm->modelConv, source, maskVal, markVal)) {
 	return false;
     }
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceFitSet.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceFitSet.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceFitSet.c	(revision 37066)
@@ -32,11 +32,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourcePhotometry.h"
@@ -222,5 +223,5 @@
     float *paramOne = params + nParamBase;
     float *betaOne = betas + nParamBase;
-    bool status = model->modelLimits (mode, nParamOne, paramOne, betaOne);
+    bool status = model->class->modelLimits (mode, nParamOne, paramOne, betaOne);
     return status;
 }
@@ -352,5 +353,11 @@
         // set the model success or failure status
         model->flags |= PM_MODEL_STATUS_FITTED;
-        if (!fitStatus) model->flags |= PM_MODEL_STATUS_NONCONVERGE;
+	if (!fitStatus) {
+	  if (isnan(myMin->value)) {
+	    model->flags |= PM_MODEL_STATUS_NAN_CHISQ;
+	  } else {
+	    model->flags |= PM_MODEL_STATUS_NONCONVERGE;
+	  }
+	}
 
         // models can go insane: reject these
@@ -381,5 +388,5 @@
         psVector *derivOne = thisSet->derivSet->data[i];
 
-        chisqOne = model->modelFunc (derivOne, paramOne, x);
+        chisqOne = model->class->modelFunc (derivOne, paramOne, x);
         chisqSum += chisqOne;
     }
@@ -570,4 +577,5 @@
     myMin->gainFactorMode = options->gainFactorMode;
     myMin->chisqConvergence = options->chisqConvergence;
+    myMin->isInteractive = options->isInteractive;
 
     psImage *covar = psImageAlloc (params->n, params->n, PS_TYPE_F32);
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceGroups.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceGroups.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceGroups.c	(revision 37066)
@@ -18,11 +18,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO.c	(revision 37066)
@@ -40,11 +40,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -61,4 +62,5 @@
 static bool pmReadoutReadXFIT(pmFPAfile *file, pmReadout *readout, char * exttype, psMetadata *hduHeader, psString xfitname, psArray *sources, long *sourceIndex);
 static bool pmReadoutReadXRAD(pmFPAfile *file, pmReadout *readout, char * exttype, psMetadata *hduHeader, psString xfitname, psArray *sources, long *sourceIndex);
+static bool pmReadoutReadXGAL(pmFPAfile *file, pmReadout *readout, char * exttype, psMetadata *hduHeader, psString xfitname, psArray *sources, long *sourceIndex);
 
 // lookup the EXTNAME values used for table data and image header segments
@@ -69,4 +71,5 @@
 			psString *xfitname,    // Extension name for extended fitted measurements
 			psString *xradname,    // Extension name for radial apertures
+			psString *xgalname,    // Extension name for galaxy shapes
 			const pmFPAfile *file, // File of interest
 			const pmFPAview *view  // View to level of interest
@@ -140,4 +143,14 @@
         }
         *xradname = pmFPAfileNameFromRule (rule, file, view);
+    }
+
+    // EXTNAME for radial apertures
+    if (xgalname) {
+        const char *rule = psMetadataLookupStr(&status, menu, "CMF.XGAL");
+        if (!rule) {
+            psError(PS_ERR_UNKNOWN, true, "missing entry for CMF.XGAL in EXTNAME.RULES in camera.config");
+            return false;
+        }
+        *xgalname = pmFPAfileNameFromRule (rule, file, view);
     }
 
@@ -352,5 +365,5 @@
 # define PM_SOURCES_WRITE(NAME,TYPE)					\
     if (!strcmp (exttype, NAME)) {					\
-	status &= pmSourcesWrite_##TYPE(file->fits, readout, sources, file->header, outhead, dataname, recipe); \
+	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); \
@@ -362,4 +375,7 @@
 	    status &= pmSourcesWrite_##TYPE##_XRAD (file->fits, readout, sources, file->header, xradname, recipe); \
 	}								\
+	if (xgalname) {							\
+	    status &= pmSourcesWrite_##TYPE##_XGAL (file->fits, readout, sources, xgalname, recipe); \
+	}								\
     }
 
@@ -464,8 +480,11 @@
         }
 
-        // if this is not TRUE, the output files only contain the psf measurements.
-        bool XSRC_OUTPUT = psMetadataLookupBool(&status, recipe, "EXTENDED_SOURCE_ANALYSIS");
+        // if none of these are TRUE, the output files only contain the psf measurements.
+	bool doPetrosian = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_PETROSIAN");
+	bool doAnnuli    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ANNULI");
+        bool XSRC_OUTPUT = doPetrosian || doAnnuli;
         bool XFIT_OUTPUT = psMetadataLookupBool(&status, recipe, "EXTENDED_SOURCE_FITS");
         bool XRAD_OUTPUT = psMetadataLookupBool(&status, recipe, "RADIAL_APERTURES");
+        bool XGAL_OUTPUT = psMetadataLookupBool(&status, recipe, "GALAXY_SHAPES");
 
         // define the EXTNAME values for the different data segments:
@@ -476,8 +495,10 @@
         psString xfitname = NULL;
         psString xradname = NULL;
+        psString xgalname = NULL;
         if (!pmSourceIOextnames(&headname, &dataname, &deteffname, 
 				XSRC_OUTPUT ? &xsrcname : NULL,
 				XFIT_OUTPUT ? &xfitname : NULL, 
 				XRAD_OUTPUT ? &xradname : NULL, 
+				XGAL_OUTPUT ? &xgalname : NULL, 
 				file, view)) {
             return false;
@@ -563,7 +584,10 @@
 		psMetadataAddStr (outhead, PS_LIST_TAIL, "XRADNAME", PS_META_REPLACE, "name of XRAD table extension", xradname);
             }
+            if (xgalname) {
+		psMetadataAddStr (outhead, PS_LIST_TAIL, "XGALNAME", PS_META_REPLACE, "name of XGAL table extension", xgalname);
+            }
 
             // these are case-sensitive since the EXTYPE is case-sensitive
-            status = true;
+            status = false;
 	    PM_SOURCES_WRITE("SMPDATA",   SMPDATA);
 	    PM_SOURCES_WRITE("PS1_DEV_0", PS1_DEV_0);
@@ -574,9 +598,12 @@
 	    PM_SOURCES_WRITE("PS1_V3",    CMF_PS1_V3);
 	    PM_SOURCES_WRITE("PS1_V4",    CMF_PS1_V4);
+	    PM_SOURCES_WRITE("PS1_V5",    CMF_PS1_V5);
 	    PM_SOURCES_WRITE("PS1_SV1",   CMF_PS1_SV1);
 	    PM_SOURCES_WRITE("PS1_SV2",   CMF_PS1_SV2);
+	    PM_SOURCES_WRITE("PS1_SV3",   CMF_PS1_SV3);
 	    PM_SOURCES_WRITE("PS1_DV1",   CMF_PS1_DV1);
 	    PM_SOURCES_WRITE("PS1_DV2",   CMF_PS1_DV2);
 	    PM_SOURCES_WRITE("PS1_DV3",   CMF_PS1_DV3);
+	    PM_SOURCES_WRITE("PS1_DV4",   CMF_PS1_DV4);
 
 	    psFree (outhead);
@@ -609,4 +636,5 @@
 	psFree (xfitname);
 	psFree (xradname);
+	psFree (xgalname);
 	psFree (deteffname);
 
@@ -620,7 +648,28 @@
 	psFree (xfitname);
 	psFree (xradname);
+	psFree (xgalname);
 	psFree (deteffname);
 	return false;
 
+      case PM_FPA_FILE_CFF: {
+        // determine the output table format
+        psMetadata *recipe = psMetadataLookupMetadata(&status, config->recipes, "PSPHOT");
+        if (!status) {
+	    psError(PS_ERR_UNKNOWN, true, "missing recipe PSPHOT in config data");
+	    return false;
+        }
+
+        hdu = pmFPAviewThisHDU (view, fpa);
+        pmConfigConformHeader(hdu->header, file->format);
+        psFitsWriteBlank (file->fits, hdu->header, NULL);
+        file->header = hdu->header;
+        file->wrote_phu = true;
+	if (!pmSourcesWrite_CFF(readout, file->fits, sources, hdu->header, recipe)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to write CFF");
+            return false;
+        }
+        break;
+      }
+        
       default:
         fprintf (stderr, "warning: type mismatch\n");
@@ -914,4 +963,16 @@
     psArray *sources = NULL;
     pmHDU *hdu;
+
+    // define the EXTNAME values for the different data segments:
+    psString headname = NULL;
+    psString dataname = NULL;
+    psString deteffname = NULL;
+    psString xsrcname = NULL;
+    psString xfitname = NULL;
+    psString xradname = NULL;
+    psString xgalname = NULL;
+
+    psMetadata *tableHeader = NULL;
+    char *xtension = NULL;
 
     switch (file->type) {
@@ -963,12 +1024,4 @@
         hdu = pmFPAviewThisHDU (view, file->fpa);
 
-        // define the EXTNAME values for the different data segments:
-        psString headname = NULL;
-        psString dataname = NULL;
-        psString deteffname = NULL;
-        psString xsrcname = NULL;
-        psString xfitname = NULL;
-        psString xradname = NULL;
-
         // determine the output table format. Assume if we need to output extendend source
         // parameters that they may exist in the input. 
@@ -981,8 +1034,12 @@
         }
 
-        // if this is not TRUE, the output files only contain the psf measurements.
-        bool XSRC_OUTPUT = psMetadataLookupBool(&status, recipe, "EXTENDED_SOURCE_ANALYSIS");
+        // if none of these are TRUE, we only read the psf measurements
+        // XXX: shouldn't we look for these extensions and read the regardless of the recipe values?
+	bool doPetrosian = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_PETROSIAN");
+	bool doAnnuli    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ANNULI");
+        bool XSRC_OUTPUT = doPetrosian || doAnnuli;
         bool XFIT_OUTPUT = psMetadataLookupBool(&status, recipe, "EXTENDED_SOURCE_FITS");
         bool XRAD_OUTPUT = psMetadataLookupBool(&status, recipe, "RADIAL_APERTURES");
+        bool XGAL_OUTPUT = psMetadataLookupBool(&status, recipe, "GALAXY_SHAPES");
 
         if (!pmSourceIOextnames(&headname, &dataname, &deteffname, 
@@ -990,4 +1047,5 @@
                 XFIT_OUTPUT ? &xfitname : NULL, 
                 XRAD_OUTPUT ? &xradname : NULL,
+                XGAL_OUTPUT ? &xgalname : NULL,
                 file, view)) {
             return false;
@@ -1033,8 +1091,8 @@
         }
 
-        psMetadata *tableHeader = psFitsReadHeader(NULL, file->fits); // The FITS header
+        tableHeader = psFitsReadHeader(NULL, file->fits); // The FITS header
         if (!tableHeader) psAbort("cannot read table header");
 
-        char *xtension = psMetadataLookupStr (NULL, tableHeader, "XTENSION");
+        xtension = psMetadataLookupStr (NULL, tableHeader, "XTENSION");
         if (!xtension) psAbort("cannot read table type");
 	if (strcmp (xtension, "BINTABLE")) {
@@ -1062,12 +1120,24 @@
 	    PM_SOURCES_READ_PSF("PS1_V3",    CMF_PS1_V3);
 	    PM_SOURCES_READ_PSF("PS1_V4",    CMF_PS1_V4);
+	    PM_SOURCES_READ_PSF("PS1_V5",    CMF_PS1_V5);
 	    PM_SOURCES_READ_PSF("PS1_SV1",   CMF_PS1_SV1);
 	    PM_SOURCES_READ_PSF("PS1_SV2",   CMF_PS1_SV2);
+	    PM_SOURCES_READ_PSF("PS1_SV3",   CMF_PS1_SV3);
 	    PM_SOURCES_READ_PSF("PS1_DV1",   CMF_PS1_DV1);
 	    PM_SOURCES_READ_PSF("PS1_DV2",   CMF_PS1_DV2);
 	    PM_SOURCES_READ_PSF("PS1_DV3",   CMF_PS1_DV3);
+	    PM_SOURCES_READ_PSF("PS1_DV4",   CMF_PS1_DV4);
+
+            if (!sources) {
+                psError(PS_ERR_IO, false, "reading CMF data from %s with format %s\n", file->filename, exttype);
+		return false;
+            }
 
             long *sourceIndex = NULL;
-            if (XSRC_OUTPUT || XFIT_OUTPUT || XRAD_OUTPUT) {
+            if (XSRC_OUTPUT || XFIT_OUTPUT || XRAD_OUTPUT || XGAL_OUTPUT) {
+                // Build sourceIndex. Lookup table from source->seq to index in sources array.
+                // Consists of an array of length max(source->seq) + 1.
+
+                // find maximum sequence number
                 long seq_max = -1;
                 for (long i = sources->n -1; i >= 0; i--) {
@@ -1082,8 +1152,10 @@
                     }
                 }
+                // allocate and initialize the index
                 sourceIndex = psAlloc((seq_max + 1) * sizeof(long));
                 for (long i = 0; i < seq_max; i++) {
                     sourceIndex[i] = -1;
                 }
+                // populate the index
                 for (long i = 0; i < sources->n; i++) {
                     pmSource *source = sources->data[i];
@@ -1111,4 +1183,11 @@
                 }
                 psFree(xradname);
+            }
+            if (XGAL_OUTPUT && xgalname) {
+		// a cmf file may have an XGAL extension, but it is not required
+                if (!pmReadoutReadXGAL(file, readout, exttype, hdu->header, xgalname, sources, sourceIndex)) {
+		    // do anything?
+                }
+                psFree(xgalname);
             }
             psFree(sourceIndex);
@@ -1132,4 +1211,61 @@
         break;
 
+      case PM_FPA_FILE_CFF: {
+        // determine the output table format
+        psMetadata *recipe = psMetadataLookupMetadata(&status, config->recipes, "PSPHOT");
+        if (!status) {
+	    psError(PS_ERR_UNKNOWN, true, "missing recipe PSPHOT in config data");
+	    return false;
+        }
+        // read in header, if not yet loaded
+        hdu = pmFPAviewThisHDU (view, file->fpa);
+
+	// look these up in the camera config?
+	// headrule = {CHIP.NAME}.hdr
+	// datarule = {CHIP.NAME}.cff
+
+        // define the EXTNAME values for the different data segments:
+        headname = pmFPAfileNameFromRule("{CHIP.NAME}.hdr", file, view);
+        dataname = pmFPAfileNameFromRule("{CHIP.NAME}.cff", file, view);
+
+        // advance to the IMAGE HEADER extension
+        if (hdu->header == NULL) {
+            // if the IMAGE header does not exist, we have no data for this view
+            if (!psFitsMoveExtNameClean (file->fits, headname)) {
+                readout->data_exists = false;
+                psFree (headname);
+                psFree (dataname);
+                return true;
+            }
+            hdu->header = psFitsReadHeader (NULL, file->fits);
+        }
+
+        // advance to the table data extension
+        // since we have read the IMAGE header, the TABLE header should exist
+        if (!psFitsMoveExtName (file->fits, dataname)) {
+            psAbort("cannot find data extension %s in %s", dataname, file->filename);
+        }
+
+        tableHeader = psFitsReadHeader(NULL, file->fits); // The FITS header
+        if (!tableHeader) psAbort("cannot read table header");
+
+	// verify this is a binary table
+        char *xtension = psMetadataLookupStr (NULL, tableHeader, "XTENSION");
+        if (!xtension) psAbort("cannot read table type");
+	if (strcmp (xtension, "BINTABLE")) {
+	    psWarning ("no binary table in extension %s, skipping\n", dataname);
+            psFree(tableHeader);
+	    return false;
+	}
+
+	sources = pmSourcesRead_CFF(file->fits, hdu->header, recipe);
+
+        psTrace("psModules.objects", 6, "read CMF table from %s : %s : %s", file->filename, headname, dataname);
+        psFree (headname);
+        psFree (dataname);
+        psFree (tableHeader);
+        }
+        break;
+
       default:
         fprintf (stderr, "warning: type mismatch\n");
@@ -1272,9 +1408,12 @@
 	PM_SOURCES_READ_XSRC("PS1_V3",    CMF_PS1_V3);
 	PM_SOURCES_READ_XSRC("PS1_V4",    CMF_PS1_V4);
+	PM_SOURCES_READ_XSRC("PS1_V5",    CMF_PS1_V5);
 	PM_SOURCES_READ_XSRC("PS1_SV1",   CMF_PS1_SV1);
 	PM_SOURCES_READ_XSRC("PS1_SV2",   CMF_PS1_SV2);
+	PM_SOURCES_READ_XSRC("PS1_SV3",   CMF_PS1_SV3);
 	PM_SOURCES_READ_XSRC("PS1_DV1",   CMF_PS1_DV1);
 	PM_SOURCES_READ_XSRC("PS1_DV2",   CMF_PS1_DV2);
 	PM_SOURCES_READ_XSRC("PS1_DV3",   CMF_PS1_DV3);
+	PM_SOURCES_READ_XSRC("PS1_DV4",   CMF_PS1_DV4);
     }
     psFree(tableHeader);
@@ -1311,9 +1450,12 @@
 	PM_SOURCES_READ_XFIT("PS1_V3",    CMF_PS1_V3);
 	PM_SOURCES_READ_XFIT("PS1_V4",    CMF_PS1_V4);
+	PM_SOURCES_READ_XFIT("PS1_V5",    CMF_PS1_V5);
 	PM_SOURCES_READ_XFIT("PS1_SV1",   CMF_PS1_SV1);
 	PM_SOURCES_READ_XFIT("PS1_SV2",   CMF_PS1_SV2);
+	PM_SOURCES_READ_XFIT("PS1_SV3",   CMF_PS1_SV3);
 	PM_SOURCES_READ_XFIT("PS1_DV1",   CMF_PS1_DV1);
 	PM_SOURCES_READ_XFIT("PS1_DV2",   CMF_PS1_DV2);
 	PM_SOURCES_READ_XFIT("PS1_DV3",   CMF_PS1_DV3);
+	PM_SOURCES_READ_XFIT("PS1_DV4",   CMF_PS1_DV4);
     }
     psFree(tableHeader);
@@ -1349,11 +1491,55 @@
 	PM_SOURCES_READ_XRAD("PS1_V3",    CMF_PS1_V3);
 	PM_SOURCES_READ_XRAD("PS1_V4",    CMF_PS1_V4);
+	PM_SOURCES_READ_XRAD("PS1_V5",    CMF_PS1_V5);
 	PM_SOURCES_READ_XRAD("PS1_SV1",   CMF_PS1_SV1);
 	PM_SOURCES_READ_XRAD("PS1_SV2",   CMF_PS1_SV2);
+	PM_SOURCES_READ_XRAD("PS1_SV3",   CMF_PS1_SV3);
 	PM_SOURCES_READ_XRAD("PS1_DV1",   CMF_PS1_DV1);
 	PM_SOURCES_READ_XRAD("PS1_DV2",   CMF_PS1_DV2);
 	PM_SOURCES_READ_XRAD("PS1_DV3",   CMF_PS1_DV3);
+	PM_SOURCES_READ_XRAD("PS1_DV4",   CMF_PS1_DV4);
     }
     psFree(tableHeader);
     return status;
 }
+static bool pmReadoutReadXGAL(pmFPAfile *file, pmReadout *readout, char *exttype, psMetadata *hduHeader, psString xgalname, psArray *sources, long *sourceIndex) 
+{
+    if (!psFitsMoveExtNameClean (file->fits, xgalname)) {
+        psTrace ("pmFPAfile", 1, "cannot find xgal extension %s in %s, skipping", xgalname, file->filename);
+        return false;
+    }
+
+    psMetadata *tableHeader = psFitsReadHeader(NULL, file->fits); // The FITS header
+    if (!tableHeader) psAbort("cannot read table header");
+
+    char *xtension = psMetadataLookupStr (NULL, tableHeader, "XTENSION");
+    if (!xtension) psAbort("cannot read table type");
+    if (strcmp (xtension, "BINTABLE")) {
+        psFree(tableHeader);
+        psWarning ("no binary table in extension %s, skipping\n", xgalname);
+        return false;
+    }
+
+# define PM_SOURCES_READ_XGAL(NAME,TYPE)				\
+    if (!strcmp (exttype, NAME)) {					\
+	status = pmSourcesRead_##TYPE##_XGAL(file->fits, readout, hduHeader, tableHeader, sources, sourceIndex); \
+    }									
+
+    bool status = false;
+    if (file->type == PM_FPA_FILE_CMF) {
+	PM_SOURCES_READ_XGAL("PS1_V1",    CMF_PS1_V1);
+	PM_SOURCES_READ_XGAL("PS1_V2",    CMF_PS1_V2);
+	PM_SOURCES_READ_XGAL("PS1_V3",    CMF_PS1_V3);
+	PM_SOURCES_READ_XGAL("PS1_V4",    CMF_PS1_V4);
+	PM_SOURCES_READ_XGAL("PS1_V5",    CMF_PS1_V5);
+	PM_SOURCES_READ_XGAL("PS1_SV1",   CMF_PS1_SV1);
+	PM_SOURCES_READ_XGAL("PS1_SV2",   CMF_PS1_SV2);
+	PM_SOURCES_READ_XGAL("PS1_SV3",   CMF_PS1_SV3);
+	PM_SOURCES_READ_XGAL("PS1_DV1",   CMF_PS1_DV1);
+	PM_SOURCES_READ_XGAL("PS1_DV2",   CMF_PS1_DV2);
+	PM_SOURCES_READ_XGAL("PS1_DV3",   CMF_PS1_DV3);
+	PM_SOURCES_READ_XGAL("PS1_DV4",   CMF_PS1_DV4);
+    }
+    psFree(tableHeader);
+    return status;
+}
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO.h	(revision 37066)
@@ -21,8 +21,10 @@
   bool pmSourcesWrite_##TYPE##_XFIT(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname); \
   bool pmSourcesWrite_##TYPE##_XRAD(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe); \
+  bool pmSourcesWrite_##TYPE##_XGAL(psFits *fits, pmReadout *readout, psArray *sources, char *extname, psMetadata *recipe); \
   psArray *pmSourcesRead_##TYPE (psFits *fits, psMetadata *header); \
   bool pmSourcesRead_##TYPE##_XSRC (psFits *fits, pmReadout *readout, psMetadata *header, psMetadata *tableHeader, psArray *sources, long *index); \
   bool pmSourcesRead_##TYPE##_XFIT (psFits *fits, pmReadout *readout, psMetadata *header, psMetadata *tableHeader, psArray *sources, long *index); \
   bool pmSourcesRead_##TYPE##_XRAD (psFits *fits, pmReadout *readout, psMetadata *header, psMetadata *tableHeader, psArray *sources, long *index);\
+  bool pmSourcesRead_##TYPE##_XGAL (psFits *fits, pmReadout *readout, psMetadata *header, psMetadata *tableHeader, psArray *sources, long *index);\
   
 // All of these functions need to use the same API, even if not all elements are used in a specific case
@@ -35,9 +37,12 @@
 MK_PROTO(CMF_PS1_V3);
 MK_PROTO(CMF_PS1_V4);
+MK_PROTO(CMF_PS1_V5);
 MK_PROTO(CMF_PS1_SV1);
 MK_PROTO(CMF_PS1_SV2);
+MK_PROTO(CMF_PS1_SV3);
 MK_PROTO(CMF_PS1_DV1);
 MK_PROTO(CMF_PS1_DV2);
 MK_PROTO(CMF_PS1_DV3);
+MK_PROTO(CMF_PS1_DV4);
 
 int pmSourceGetDophotType (pmSource *source);
@@ -52,4 +57,6 @@
 
 psArray *pmSourcesReadCMP (char *filename, psMetadata *header);
+psArray *pmSourcesRead_CFF (psFits *fits, psMetadata *header, psMetadata *recipe);
+bool pmSourcesWrite_CFF (pmReadout *readout, psFits *fits, psArray *sources, psMetadata *header, psMetadata *recipe);
 
 bool pmSourcesWritePSFs (psArray *sources, char *filename);
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_CFF.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_CFF.c	(revision 37066)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_CFF.c	(revision 37066)
@@ -0,0 +1,551 @@
+/** @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 "pmModelClass.h"
+#include "pmModel.h"
+#include "pmModelUtils.h"
+#include "pmSourceMasks.h"
+#include "pmSourceExtendedPars.h"
+#include "pmSourceDiffStats.h"
+#include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
+#include "pmSource.h"
+#include "pmSourceFitModel.h"
+#include "pmPSF.h"
+#include "pmPSFtry.h"
+
+#include "pmSourceIO.h"
+#include "pmSourceOutputs.h"
+
+// read in sources readout from a cff fits file
+psArray *pmSourcesRead_CFF (psFits *fits, psMetadata *header, psMetadata *recipe)
+{
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_PTR_NON_NULL(header, false);
+
+    bool status;
+    psEllipseAxes axes;
+
+    // define PSF model type
+    // XXX need to carry the extra model parameters
+    int modelType = pmModelClassGetType ("PS_MODEL_GAUSS");
+
+    // Read lookup table for model classes (if defined)
+    pmModelClassReadHeader(header);
+
+    char *PSF_NAME = psMetadataLookupStr (&status, header, "PSFMODEL");
+    if (PSF_NAME != NULL) {
+        modelType = pmModelClassGetType (PSF_NAME);
+    }
+    PS_ASSERT_INT_NONNEGATIVE(modelType, NULL);
+
+    pmModelType sersicModelType = pmModelClassGetType("PS_MODEL_SERSIC");
+    pmModelType devModelType    = pmModelClassGetType("PS_MODEL_DEV");
+
+    psString modelForce = psMetadataLookupStr(&status, recipe, "EXT_MODEL_TYPE_FORCE");
+    psF32 forceDevSersicMin = NAN;
+    bool forceAll = false;
+    int modelTypeForce = 0;
+    if (!strcmp(modelForce, "ALL")) {
+        forceAll = true;
+    } else if (!strcmp(modelForce, "PS_MODEL_SERSIC")) {
+        modelTypeForce = sersicModelType;
+        forceDevSersicMin = psMetadataLookupF32(&status, recipe, "EXT_MODEL_FORCE_DEV_SERSIC_MIN");
+    } else {
+        modelTypeForce = pmModelClassGetType(modelForce);
+        PS_ASSERT_INT_NONNEGATIVE(modelTypeForce, NULL);
+    }
+
+    // skip extended model types for likely stars
+    // max value of KronMag - psfMag to keep ...
+    psF32 starCut = psMetadataLookupF32(&status, recipe, "EXT_MODEL_FORCE_MAGDIFF_MAX");
+    if (!status) {
+        starCut = 0;
+    }
+    // ... unless SN is less than this value
+    psF32 SNMinForCut = psMetadataLookupF32(&status, recipe, "EXT_MODEL_FORCE_CUT_SN_MIN");
+    if (!status) {
+        SNMinForCut = 10;
+    }
+
+    // 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 numRows = psFitsTableSize(fits); // Number of rows in table
+    psArray *sources = psArrayAllocEmpty(numRows); // Array of sources, to return
+
+    // convert the table to the pmSource entries
+    pmSource *source = NULL;
+    for (int i = 0; i < numRows; 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;
+        }
+
+	// here are the things we read from the FITS table (XXX modify names if needed)
+
+	unsigned int ID  = psMetadataLookupU32 (&status, row, "ID");
+	float X          = psMetadataLookupF32 (&status, row, "X");
+        assert(status);  // poorly constructed cff
+	float Y          = psMetadataLookupF32 (&status, row, "Y");
+        assert(status);  // poorly constructed cff
+        float flux       = psMetadataLookupF32 (&status, row, "FLUX"); // guess at the instrumental flux
+        if (!isfinite(X) || !isfinite(Y) || !isfinite(flux)) {
+            psError(PS_ERR_IO, true, "source ID %d is invalid x: %f y: %f flux: %f", ID, X, Y, flux);
+            psFree(sources);
+            return NULL;
+        }
+
+
+        float apRadius   = psMetadataLookupF32 (&status, row, "AP_RADIUS");
+        float kronRadius = psMetadataLookupF32 (&status, row, "KRON_RADIUS");
+        float petRadius  = psMetadataLookupF32 (&status, row, "PETRO_RADIUS");
+        float SN         = psMetadataLookupF32 (&status, row, "SN");
+        bool fitGalaxy   = psMetadataLookupU8 (&status, row, "FIT_GALAXY");
+        bool psfStar     = psMetadataLookupU8 (&status, row, "PSF_STAR");
+
+        float Rmajor     = psMetadataLookupF32 (&status, row, "R_MAJOR");
+        float Rminor     = psMetadataLookupF32 (&status, row, "R_MINOR");
+        float theta      = psMetadataLookupF32 (&status, row, "THETA");
+        float chisq      = psMetadataLookupF32 (&status, row, "CHISQ");
+        float nDOF       = psMetadataLookupF32 (&status, row, "NDOF");
+        float magDiff    = psMetadataLookupF32 (&status, row, "MAG_DIFF");
+        psS16 modelFlags = psMetadataLookupS32 (&status, row, "MODEL_FLAGS");
+
+        int   galaxyModelType = psMetadataLookupS32(&status, row, "MODEL_TYPE");
+        if (status && galaxyModelType >= 0) {
+            galaxyModelType = pmModelClassGetLocalType(galaxyModelType);
+        } else {
+            galaxyModelType = -1;
+        }
+        float Sindex     = psMetadataLookupF32 (&status, row, "INDEX"); // Should this be PAR_07 not sersic index
+
+        if (!source || ID != source->seq) {
+            if (source) {
+                psArrayAdd (sources, 1, source);
+                psFree(source);
+            }
+            source = pmSourceAlloc ();
+            pmModel *model = pmModelAlloc (modelType);
+            source->modelPSF  = model;
+            //        RoughClass wants source type to be unknown
+            //        source->type = PM_SOURCE_TYPE_STAR; // XXX this should be added to the flags
+            source->type = PM_SOURCE_TYPE_UNKNOWN;
+
+            // XXX we can set this in general, but for a specific image, we need to weed out SATSTARS and
+            // stars that are masked
+            if (psfStar) {
+                source->tmpFlags |= PM_SOURCE_TMPF_CANDIDATE_PSFSTAR;
+            }
+
+            // NOTE: A SEGV here because "model" is NULL is probably caused by not initialising the models.
+            psF32 *PAR = model->params->data.F32;
+            psF32 *dPAR = model->dparams->data.F32;
+
+            source->seq       = ID;
+
+            PAR[PM_PAR_XPOS]  = X;
+            PAR[PM_PAR_YPOS]  = Y;
+
+            dPAR[PM_PAR_XPOS] = 0.0;
+            dPAR[PM_PAR_YPOS] = 0.0;
+
+            PAR[PM_PAR_SKY]   = 0.0;
+            dPAR[PM_PAR_SKY]  = 0.0;
+
+            PAR[PM_PAR_I0]    = 1.0;
+            dPAR[PM_PAR_I0]   = 0.0;
+
+            source->sky       = PAR[PM_PAR_SKY];
+            source->skyErr    = dPAR[PM_PAR_SKY];
+
+            source->psfMag    = 0.0;
+            source->psfMagErr = 0.0;
+            source->apMag     = 0.0;
+            source->apRadius  = apRadius;
+
+            // we generate a somewhat fake PSF model here -- 
+            // in most (all?) contexts, we will replace this with a measured psf model
+            // elsewhere
+            axes.major        = 1.0;
+            axes.minor        = 1.0;
+            axes.theta        = 0.0;
+            pmPSF_AxesToModel (PAR, axes, model->class->useReff);
+
+            // peak->detValue, rawFlux, smoothFlux are all set to the flux argument which is counts per second
+            source->peak      = pmPeakAlloc(X, Y, flux, PM_PEAK_LONE);
+            source->peak->xf  = X; // pmPeakAlloc converts X,Y to int, so reset here
+            source->peak->yf  = Y; // pmPeakAlloc converts X,Y to int, so reset here
+            source->peak->dx  = 0.0;
+            source->peak->dy  = 0.0;
+
+            source->extSN     = SN;
+
+            source->moments = pmMomentsAlloc ();
+            source->moments->Mx = X;
+            source->moments->My = Y;
+            source->moments->Mrf = kronRadius * 0.4; // kronRadius is 2.5 * first radial moment
+
+            // Don't mark the moments as measured because that causes many fields to be left blank.
+            // The moments code knows not to change the position or the Mrf for external sources
+            // source->tmpFlags |= PM_SOURCE_TMPF_MOMENTS_MEASURED;
+
+            if (isfinite(petRadius)) {
+                source->extpars = pmSourceExtendedParsAlloc ();
+                source->extpars->petrosianRadius = petRadius;
+            }
+
+        }
+        bool saveExtModelParams = false;
+        if (fitGalaxy && galaxyModelType >= 0) {
+            // skip likely stars 
+            if (magDiff < starCut || SN < SNMinForCut) {
+                if (forceAll) {
+                    saveExtModelParams = true;
+                } else if (galaxyModelType == modelTypeForce) {
+                    // This is the model type that we are looking for
+                    // proceed
+                    saveExtModelParams = true;
+                } else if (modelTypeForce == sersicModelType && galaxyModelType == devModelType) {
+                    // We're doing sersic models, if sersic index is greater than the recipe's minimum index
+                    // do dev model as well
+                    if (isfinite(forceDevSersicMin) || Sindex >= forceDevSersicMin) {
+                        saveExtModelParams = true;
+                    }
+                } else {
+                    // not interested in this model
+                }
+            }
+        }
+
+        if (saveExtModelParams) {
+            if (!source->modelFits) {
+                source->modelFits = psArrayAllocEmpty (1);
+            }
+            pmModel *model = pmModelAlloc(galaxyModelType);
+            psF32 *xPAR = model->params->data.F32;
+
+            xPAR[PM_PAR_SKY]  = 0.0;
+            xPAR[PM_PAR_I0]   = 1.0;
+            xPAR[PM_PAR_XPOS] = X;
+	    xPAR[PM_PAR_YPOS] = Y;
+	    
+	    psEllipseAxes galaxyAxes;
+	    galaxyAxes.major = Rmajor;
+	    galaxyAxes.minor = Rminor;
+	    galaxyAxes.theta = theta * PS_RAD_DEG;
+
+	    pmPSF_AxesToModel (xPAR, galaxyAxes, model->class->useReff);
+	    if (model->params->n > 7) {
+                xPAR[PM_PAR_7] = 0.5 / Sindex;
+	    }
+
+            model->chisq = chisq;
+            model->nDOF = nDOF;
+            model->flags = modelFlags;
+
+	    psArrayAdd (source->modelFits, 1, model);
+
+	    psFree (model);
+        }
+
+        psFree(row);
+    }
+    if (source) {
+        // close out last source
+        psArrayAdd (sources, 1, source);
+        psFree(source);
+    }
+
+    return sources;
+}
+
+bool pmSourcesWrite_CFF(pmReadout *readout, psFits *fits, psArray *sources, psMetadata *header, psMetadata *recipe) {
+    
+    char *extname = "SkyChip.cff";
+    
+    bool mdok;
+    psF32 exptime = psMetadataLookupF32(&mdok, readout->parent->concepts, "CELL.EXPOSURE");
+    PS_ASSERT(mdok, false);
+
+    // write the definition of the model class type values to the header
+    psMetadata *outputHeader = psMetadataAlloc();
+    pmModelClassWriteHeader(outputHeader);
+
+    psArray *table = psArrayAllocEmpty(sources->n);
+
+    pmModelType sersicModelType = pmModelClassGetType("PS_MODEL_SERSIC");
+    pmModelType devModelType    = pmModelClassGetType("PS_MODEL_DEV");
+    pmModelType selectedModelType = -1;
+    bool chooseBest = false;
+    bool chooseAll = false;
+
+    psString modelToChoose = psMetadataLookupStr(&mdok, recipe, "EXT_MODEL_TYPE_FOR_CFF");
+
+    if (mdok && modelToChoose != NULL) {
+        if (!strcmp(modelToChoose, "BEST")) {
+            chooseBest = true;
+        } else if (!strcmp(modelToChoose, "ALL")) {
+            chooseAll = true;
+        } else if (strcmp(modelToChoose, "PS_MODEL_SERSIC")) {
+            // We have selected a model type other than Sersic. 
+            // Save it's type for use below.  Sersic is handled specially
+            selectedModelType = pmModelClassGetType(modelToChoose);
+        }
+    }
+
+    // minimum sersic index to force devModel
+    psF32 sersicMinDev = psMetadataLookupF32(&mdok, recipe, "EXT_MODEL_FORCE_DEV_SERSIC_MIN");
+    if (!mdok) {
+        sersicMinDev = NAN;
+    }
+
+    sources = psArraySort (sources, pmSourceSortBySeq);
+
+    for (int i = 0; i < sources->n; i++) {
+        pmSource *thisSource = sources->data[i];
+        pmSource *source = thisSource->parent ? thisSource->parent : thisSource;
+
+        #define MAX_ROWS_PER_SRC 10
+        psF32 xPos[MAX_ROWS_PER_SRC], yPos[MAX_ROWS_PER_SRC], flux[MAX_ROWS_PER_SRC];
+        psF32 rMajor[MAX_ROWS_PER_SRC], rMinor[MAX_ROWS_PER_SRC], theta[MAX_ROWS_PER_SRC];
+        psF32 chisq[MAX_ROWS_PER_SRC], nDOF[MAX_ROWS_PER_SRC];
+        psS32 modelFlags[MAX_ROWS_PER_SRC];
+        psS32 modelType[MAX_ROWS_PER_SRC];
+        psF32 sersicIndex = NAN;
+        bool fitGalaxy = false;
+        bool psfStar = (source->mode & PM_SOURCE_MODE_PSFSTAR) ? true : false;
+        int n_rows = 0;
+
+        psF32 kronFlux = source->moments->KronFlux;
+        psF32 SN = NAN;
+        psF32 magDiff = NAN;
+        if (isfinite(kronFlux) && isfinite(source->moments->KronFluxErr) && isfinite(source->psfMag)) {
+            SN = kronFlux/source->moments->KronFluxErr;
+            // kronMag - psfMag for use as star/glaxy separator
+            magDiff = -2.5 * log10(kronFlux) - source->psfMag ;
+        }
+
+        // start with psf model
+        pmModel *model = source->modelPSF;
+        if (model == NULL) continue;
+        psF32 *PAR = model->params->data.F32;
+        if (!isfinite(PAR[PM_PAR_SXX]) || !isfinite(PAR[PM_PAR_SYY])  || !isfinite(PAR[PM_PAR_SXY]) ||
+            !isfinite(source->psfFlux)) {
+            continue;
+        }
+
+        xPos[0] = model->params->data.F32[PM_PAR_XPOS];
+        yPos[0] = model->params->data.F32[PM_PAR_YPOS];
+        flux[0] = source->psfFlux;
+        rMajor[0] = 0;
+        rMinor[0] = 0;
+        theta[0] = 0;
+        modelType[0] = -1;
+        sersicIndex = NAN;
+        chisq[0] = NAN;
+        nDOF[0] = NAN;
+        modelFlags[0] = 0;
+
+        if (source->modelFits != NULL) {
+            // figure out which models to use based on recipe paramters
+            if (chooseAll) {
+                // Save parameters for all valid extended models
+
+                // but make sure we aren't going to overflow our arrays
+                assert (source->modelFits->n < MAX_ROWS_PER_SRC);
+
+                for (int j=0; j<source->modelFits->n; j++) {
+                    pmModel *model = source->modelFits->data[j];
+                    psF32 *PAR = model->params->data.F32;
+
+                    if (isfinite(PAR[PM_PAR_SXX]) && isfinite(PAR[PM_PAR_SYY])  && isfinite(PAR[PM_PAR_SXY]) &&
+                        isfinite(model->mag)) {
+
+                        xPos[n_rows] = PAR[PM_PAR_XPOS];
+                        yPos[n_rows] = PAR[PM_PAR_YPOS];
+
+                        psEllipseAxes axes = pmPSF_ModelToAxes (PAR, model->class->useReff);
+                        rMajor[n_rows] = axes.major;
+                        rMinor[n_rows] = axes.minor;
+                        theta[n_rows]  = axes.theta*PS_DEG_RAD;
+                        flux[n_rows] = pow(10.0, -0.4*model->mag);
+                        modelType[n_rows] = model->type;
+                        chisq[n_rows] = model->chisq;
+                        nDOF[n_rows] = model->nDOF;
+                        modelFlags[n_rows] = model->flags;
+                        fitGalaxy = true;
+                        if (model->params->n == 8) {
+                            // this will save the sersic index in all models but that might
+                            // be useful
+                            sersicIndex = 0.5 / PAR[PM_PAR_7];
+                        } else {
+                            sersicIndex = NAN;
+                        }
+
+                        n_rows++;
+                    }
+                }
+            } else {
+                int jModelSersic = -1;
+                int jModelDev = -1;
+                int jModelSelected = -1;
+                psF32 minChisq = NAN;
+                for (int j=0; j<source->modelFits->n; j++) {
+                    pmModel *model = source->modelFits->data[j];
+                    if (chooseBest) {
+                        // choose the model with lowest chisq
+                        if (isfinite(model->chisq) && (!isfinite(minChisq) || model->chisq < minChisq)) {
+                            jModelSelected = j;
+                            minChisq = model->chisq;
+                        }
+                    } else {
+                        // find the index of models of interest
+                        if (model->type == selectedModelType) {
+                            jModelSelected = j;
+                        } else if (model->type == sersicModelType) {
+                            jModelSersic = j;
+                        } else if (model->type == devModelType) {
+                            jModelDev = j;
+                        }
+                    }
+                }
+                if (jModelSelected >= 0 || jModelSersic >= 0) {
+                    // If a specific non-sersic model we take paramers from that one.
+                    // Otherwise we do the sersic model.
+                    pmModel *model = jModelSelected >= 0 ? source->modelFits->data[jModelSelected] :
+                                                           source->modelFits->data[jModelSersic];
+                    psF32 *PAR = model->params->data.F32;
+
+                    if (isfinite(PAR[PM_PAR_SXX]) && isfinite(PAR[PM_PAR_SYY])  && isfinite(PAR[PM_PAR_SXY]) &&
+                        isfinite(model->mag)) {
+
+                        xPos[0] = PAR[PM_PAR_XPOS];
+                        yPos[0] = PAR[PM_PAR_YPOS];
+
+                        psEllipseAxes axes = pmPSF_ModelToAxes (PAR, model->class->useReff);
+                        rMajor[0] = axes.major;
+                        rMinor[0] = axes.minor;
+                        theta[0]  = axes.theta*PS_DEG_RAD;
+                        flux[0] = pow(10.0, -0.4*model->mag);
+                        modelType[0] = model->type;
+                        chisq[0] = model->chisq;
+                        nDOF[0] = model->nDOF;
+                        modelFlags[0] = model->flags;
+                        fitGalaxy = true;
+                        if (model->type == sersicModelType) {
+                            PS_ASSERT_FLOAT_LARGER_THAN(PAR[PM_PAR_7], 0.0, false);
+                            sersicIndex = 0.5 / PAR[PM_PAR_7];
+                        }
+
+                        n_rows = 1;
+
+                        // Unless a specific non-sersic model type was selected do dev model for sources with
+                        // sersic index above the recipe limit.
+                        if (jModelSelected == -1 && jModelDev >= 0 && isfinite(sersicMinDev) &&
+                            sersicIndex > sersicMinDev) {
+
+                            model = source->modelFits->data[jModelDev];
+                            if (isfinite(PAR[PM_PAR_SXX]) && isfinite(PAR[PM_PAR_SYY])  && isfinite(PAR[PM_PAR_SXY]) &&
+                                isfinite(model->mag)) { 
+
+                                xPos[1] = PAR[PM_PAR_XPOS];
+                                yPos[1] = PAR[PM_PAR_YPOS];
+
+                                psEllipseAxes axes = pmPSF_ModelToAxes (PAR, model->class->useReff);
+                                rMajor[1] = axes.major;
+                                rMinor[1] = axes.minor;
+                                theta[1]  = axes.theta*PS_DEG_RAD;
+                                flux[1] = pow(10.0, -0.4*model->mag);
+                                modelType[1] = model->type;
+                                sersicIndex = NAN;
+                                chisq[1] = model->chisq;
+                                nDOF[1] = model->nDOF;
+                                modelFlags[1] = model->flags;
+                                n_rows = 2;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        if (n_rows == 0) {
+            // didn't find a suitable extended model we have one row with the psf parameters
+            n_rows = 1;
+        }
+
+        for (int j = 0; j < n_rows; j++) {
+            psMetadata *row = psMetadataAlloc();
+            psMetadataAddU32 (row, PS_LIST_TAIL, "ID",         0,   "IPP detection identifier",  source->seq);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "X",                0, "x coordinate",          xPos[j]);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "Y",                0, "y coordinate",          yPos[j]);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "FLUX",             0, "flux per second",       flux[j]/exptime);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "SN",               0, "kron flux signal to noise", SN);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "MAG_DIFF",         0, "psf mag - kron mag",    magDiff);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "AP_RADIUS",        0, "aperture radius",       source->apRadius);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "KRON_RADIUS",      0, "Kron radius",           source->moments->Mrf * 2.5);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "PETRO_RADIUS",     0, "Petrosian Radius",      source->extpars ? source->extpars->petrosianRadius : NAN);
+            psMetadataAddBool (row, PS_LIST_TAIL, "FIT_GALAXY",      0, "source has xfit",       fitGalaxy); 
+            psMetadataAddBool (row, PS_LIST_TAIL, "PSF_STAR",        0, "source was psf star",   psfStar);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "R_MAJOR",          0, "radius of major axis",  rMajor[j]);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "R_MINOR",          0, "radius of minor axis",  rMinor[j]);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "THETA",            0, "theta",                 theta[j]);
+            psMetadataAddS32 (row, PS_LIST_TAIL, "MODEL_TYPE",       0, "model type",            modelType[j]);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "INDEX",            0, "sersic index",          sersicIndex);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "CHISQ",            0, "chisq",                 chisq[j]);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "NDOF",             0, "n degrees of freedom",  nDOF[j]);
+            psMetadataAddS32 (row, PS_LIST_TAIL, "MODEL_FLAGS",      0, "model flags",           modelFlags[j]);
+
+            psArrayAdd(table, 100, row);
+            psFree(row);
+        }
+    }
+
+    if (!psFitsWriteTable(fits, outputHeader, table, extname)) {
+        psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
+        psFree(table);
+        psFree(outputHeader);
+        return false;
+    }
+    psFree(table);
+    psFree(outputHeader);
+
+    return true;
+}
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_CMF.c.in
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_CMF.c.in	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_CMF.c.in	(revision 37066)
@@ -37,11 +37,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -141,7 +142,8 @@
         @ALL@      		    psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG",           PS_DATA_F32, "magnitude in standard aperture",             source->apMag);
 	@>PS1_V2,PS1_SV?,>PS1_DV1@  psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG_RAW",       PS_DATA_F32, "magnitude in reported aperture",             source->apMagRaw);
-        @ALL@      		    psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG_RADIUS",    PS_DATA_F32, "radius used for aperture mags",              outputs.apRadius);
+        @ALL@      		    psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG_RADIUS",    PS_DATA_F32, "radius used for aperture mags",              source->apRadius);
 	@>PS1_DV1,>PS1_V3,>PS1_SV1@ psMetadataAdd (row, PS_LIST_TAIL, "AP_FLUX",          PS_DATA_F32, "instrumental flux in standard aperture",     source->apFlux);
 	@>PS1_DV1,>PS1_V3,>PS1_SV1@ psMetadataAdd (row, PS_LIST_TAIL, "AP_FLUX_SIG",      PS_DATA_F32, "aperture flux error",                        source->apFluxErr);
+	@>PS1_V4,>PS1_SV2,>PS1_DV3@ psMetadataAdd (row, PS_LIST_TAIL, "AP_NPIX",          PS_DATA_S32, "aperture unmasked pixels",                   source->apNpixels);
 
 	@<PS1_V3,PS1_SV1,PS1_DV?@ psMetadataAdd (row, PS_LIST_TAIL, "PEAK_FLUX_AS_MAG", PS_DATA_F32, "Peak flux expressed as magnitude",           outputs.peakMag);
@@ -162,7 +164,15 @@
         @ALL@     		  psMetadataAdd (row, PS_LIST_TAIL, "EXT_NSIGMA",       PS_DATA_F32, "Nsigma deviations from PSF to EXT",          source->extNsigma);
 
+        // PSF shape parameters:
         @ALL@     		  psMetadataAdd (row, PS_LIST_TAIL, "PSF_MAJOR",        PS_DATA_F32, "PSF width (major axis)",                     outputs.psfMajor);
         @ALL@     		  psMetadataAdd (row, PS_LIST_TAIL, "PSF_MINOR",        PS_DATA_F32, "PSF width (minor axis)",                     outputs.psfMinor);
         @ALL@     		  psMetadataAdd (row, PS_LIST_TAIL, "PSF_THETA",        PS_DATA_F32, "PSF orientation angle",                      outputs.psfTheta);
+        @>PS1_V4,>PS1_SV2,>PS1_DV3@ psMetadataAdd (row, PS_LIST_TAIL, "PSF_CORE",         PS_DATA_F32, "k term if defined",                          outputs.psfCore);
+
+	// I use a look-up table and linear interpolation to map PSF_MAJOR,PSF_MINOR + PSF_CORE to FWHM values
+        @>PS1_V4,>PS1_SV2,>PS1_DV3@ psMetadataAdd (row, PS_LIST_TAIL, "PSF_FWHM_MAJ",        PS_DATA_F32, "PSF FWHM (major axis)",                   outputs.psfMajorFWHM);
+        @>PS1_V4,>PS1_SV2,>PS1_DV3@ psMetadataAdd (row, PS_LIST_TAIL, "PSF_FWHM_MIN",        PS_DATA_F32, "PSF FWHM (minor axis)",                   outputs.psfMinorFWHM);
+
+        // psf data quality
         @ALL@     		  psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF",           PS_DATA_F32, "PSF coverage/quality factor (bad)",          source->pixWeightNotBad);
 	@>PS1_V2,PS1_SV?,>PS1_DV1@ psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF_PERFECT",   PS_DATA_F32, "PSF coverage/quality factor (poor)",         source->pixWeightNotPoor);
@@ -178,4 +188,37 @@
 	@>PS1_V2,PS1_SV?@ 	  psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M4C",      PS_DATA_F32, "fourth momemt cos theta",                    moments.M_c4);
 	@>PS1_V2,PS1_SV?@ 	  psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M4S",      PS_DATA_F32, "fourth momemt sin theta",                    moments.M_s4);
+
+	// Lensing parameters:
+	if (source->lensingOBJ && source->lensingOBJ->smear) {
+	  @>PS1_V4@               psMetadataAdd (row, PS_LIST_TAIL, "LENS_X11_SM_OBJ",  PS_DATA_F32, "smear polarizability element (objects)",     source->lensingOBJ->smear->X11);
+	  @>PS1_V4@               psMetadataAdd (row, PS_LIST_TAIL, "LENS_X12_SM_OBJ",  PS_DATA_F32, "smear polarizability element (objects)",     source->lensingOBJ->smear->X12);
+	  @>PS1_V4@               psMetadataAdd (row, PS_LIST_TAIL, "LENS_X22_SM_OBJ",  PS_DATA_F32, "smear polarizability element (objects)",     source->lensingOBJ->smear->X22);
+	  @>PS1_V4@               psMetadataAdd (row, PS_LIST_TAIL, "LENS_E1_SM_OBJ",   PS_DATA_F32, "smear polarizability element (objects)",     source->lensingOBJ->smear->e1);
+	  @>PS1_V4@               psMetadataAdd (row, PS_LIST_TAIL, "LENS_E2_SM_OBJ",   PS_DATA_F32, "smear polarizability element (objects)",     source->lensingOBJ->smear->e2);
+	}
+
+	if (source->lensingOBJ && source->lensingOBJ->shear) {
+	  @>PS1_V4@               psMetadataAdd (row, PS_LIST_TAIL, "LENS_X11_SH_OBJ",  PS_DATA_F32, "shear polarizability element (objects)",     source->lensingOBJ->shear->X11);
+	  @>PS1_V4@               psMetadataAdd (row, PS_LIST_TAIL, "LENS_X12_SH_OBJ",  PS_DATA_F32, "shear polarizability element (objects)",     source->lensingOBJ->shear->X12);
+	  @>PS1_V4@               psMetadataAdd (row, PS_LIST_TAIL, "LENS_X22_SH_OBJ",  PS_DATA_F32, "shear polarizability element (objects)",     source->lensingOBJ->shear->X22);
+	  @>PS1_V4@               psMetadataAdd (row, PS_LIST_TAIL, "LENS_E1_SH_OBJ",   PS_DATA_F32, "shear polarizability element (objects)",     source->lensingOBJ->shear->e1); 
+	  @>PS1_V4@               psMetadataAdd (row, PS_LIST_TAIL, "LENS_E2_SH_OBJ",   PS_DATA_F32, "shear polarizability element (objects)",     source->lensingOBJ->shear->e2); 
+	}
+
+	if (source->lensingPSF && source->lensingPSF->smear) {
+	  @>PS1_V4@               psMetadataAdd (row, PS_LIST_TAIL, "LENS_X11_SM_PSF",  PS_DATA_F32, "smear polarizability element (objects)",     source->lensingPSF->smear->X11);
+	  @>PS1_V4@               psMetadataAdd (row, PS_LIST_TAIL, "LENS_X12_SM_PSF",  PS_DATA_F32, "smear polarizability element (objects)",     source->lensingPSF->smear->X12);
+	  @>PS1_V4@               psMetadataAdd (row, PS_LIST_TAIL, "LENS_X22_SM_PSF",  PS_DATA_F32, "smear polarizability element (objects)",     source->lensingPSF->smear->X22);
+	  @>PS1_V4@               psMetadataAdd (row, PS_LIST_TAIL, "LENS_E1_SM_PSF",   PS_DATA_F32, "smear polarizability element (objects)",     source->lensingPSF->smear->e1); 
+	  @>PS1_V4@               psMetadataAdd (row, PS_LIST_TAIL, "LENS_E2_SM_PSF",   PS_DATA_F32, "smear polarizability element (objects)",     source->lensingPSF->smear->e2); 
+	}
+
+	if (source->lensingPSF && source->lensingPSF->shear) {
+	  @>PS1_V4@               psMetadataAdd (row, PS_LIST_TAIL, "LENS_X11_SH_PSF",  PS_DATA_F32, "shear polarizability element (objects)",     source->lensingPSF->shear->X11);
+	  @>PS1_V4@               psMetadataAdd (row, PS_LIST_TAIL, "LENS_X12_SH_PSF",  PS_DATA_F32, "shear polarizability element (objects)",     source->lensingPSF->shear->X12);
+	  @>PS1_V4@               psMetadataAdd (row, PS_LIST_TAIL, "LENS_X22_SH_PSF",  PS_DATA_F32, "shear polarizability element (objects)",     source->lensingPSF->shear->X22);
+	  @>PS1_V4@               psMetadataAdd (row, PS_LIST_TAIL, "LENS_E1_SH_PSF",   PS_DATA_F32, "shear polarizability element (objects)",     source->lensingPSF->shear->e1); 
+	  @>PS1_V4@               psMetadataAdd (row, PS_LIST_TAIL, "LENS_E2_SH_PSF",   PS_DATA_F32, "shear polarizability element (objects)",     source->lensingPSF->shear->e2); 
+	}
 
         @>PS1_V2,PS1_SV?,>PS1_DV1@ psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_R1",       PS_DATA_F32, "first radial moment",                        moments.Mrf);
@@ -268,12 +311,16 @@
 
     // 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 header does not define the model, default to a gaussian
+    char *PSF_NAME = psMetadataLookupStr (&status, header, "PSFMODEL");
     if (PSF_NAME != NULL) {
         modelType = pmModelClassGetType (PSF_NAME);
     }
     assert (modelType > -1);
+
+    // do we expect to find lensing parameters?
+    bool haveLensOBJ = psMetadataLookupBool (&status, header, "LENS_OBJ");
+    bool haveLensPSF = psMetadataLookupBool (&status, header, "LENS_PSF");
 
     // We get the size of the table, and allocate the array of sources first because the table
@@ -310,4 +357,8 @@
         @ALL@     axes.theta        = psMetadataLookupF32 (&status, row, "PSF_THETA");
         @ALL@     axes.theta        = axes.theta * PS_RAD_DEG;
+	
+	@>PS1_V4,>PS1_SV2,>PS1_DV3@ if (model->params->n > PM_PAR_7) {
+	@>PS1_V4,>PS1_SV2,>PS1_DV3@     PAR[PM_PAR_7] = psMetadataLookupF32 (&status, row, "PSF_CORE");
+	@>PS1_V4,>PS1_SV2,>PS1_DV3@ } 
 
         @ALL@     PAR[PM_PAR_SKY]   = psMetadataLookupF32 (&status, row, "SKY");
@@ -321,4 +372,6 @@
         @ALL@     source->apMag     = psMetadataLookupF32 (&status, row, "AP_MAG");
         @>PS1_V2,PS1_SV?,>PS1_DV1@ source->apMagRaw  = psMetadataLookupF32 (&status, row, "AP_MAG_RAW");
+	@>PS1_DV1,>PS1_V3,>PS1_SV1@ source->apFlux = psMetadataLookupF32 (&status, row, "AP_FLUX");
+	@>PS1_DV1,>PS1_V3,>PS1_SV1@ source->apFluxErr = psMetadataLookupF32 (&status, row, "AP_FLUX_SIG");
 
         // XXX use these to determine PAR[PM_PAR_I0] if they exist?
@@ -331,5 +384,5 @@
         @ALL@     dPAR[PM_PAR_I0]   = (isfinite(source->psfMag)) ? PAR[PM_PAR_I0] * source->psfMagErr : NAN;
 
-        pmPSF_AxesToModel (PAR, axes, modelType);
+        pmPSF_AxesToModel (PAR, axes, model->class->useReff);
 
         @ALL@     float peakMag     = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
@@ -349,5 +402,6 @@
         @ALL@     source->crNsigma  = psMetadataLookupF32 (&status, row, "CR_NSIGMA");
         @ALL@     source->extNsigma = psMetadataLookupF32 (&status, row, "EXT_NSIGMA");
-        @ALL@     source->apRadius  = psMetadataLookupS32 (&status, row, "AP_MAG_RADIUS");
+        @ALL@     source->apRadius  = psMetadataLookupF32 (&status, row, "AP_MAG_RADIUS");
+	@>PS1_V4,>PS1_SV2,>PS1_DV3@ source->apNpixels = psMetadataLookupS32 (&status, row, "AP_NPIX");
 
         // note that some older versions used PSF_PROBABILITY: this was not well defined.
@@ -376,4 +430,39 @@
         @>PS1_V2,PS1_SV?@ source->moments->Mxyyy = -0.25 * psMetadataLookupF32 (&status, row, "MOMENTS_M4S");
         @>PS1_V2,PS1_SV?@ source->moments->Myyyy = 0.0;
+
+	// Lensing parameters (on read if PS1_V5+)
+	if (haveLensOBJ) {
+	  source->lensingOBJ = pmSourceLensingAlloc ();
+	  source->lensingOBJ->smear = pmLensingParsAlloc();
+	  source->lensingOBJ->shear = pmLensingParsAlloc();
+
+	  @>PS1_V4@ source->lensingOBJ->smear->X11 = psMetadataLookupF32 (&status, row, "LENS_X11_SM_OBJ");
+	  @>PS1_V4@ source->lensingOBJ->smear->X12 = psMetadataLookupF32 (&status, row, "LENS_X12_SM_OBJ");
+	  @>PS1_V4@ source->lensingOBJ->smear->X22 = psMetadataLookupF32 (&status, row, "LENS_X22_SM_OBJ");
+	  @>PS1_V4@ source->lensingOBJ->smear->e1  = psMetadataLookupF32 (&status, row, "LENS_E1_SM_OBJ");
+	  @>PS1_V4@ source->lensingOBJ->smear->e2  = psMetadataLookupF32 (&status, row, "LENS_E2_SM_OBJ");
+	  @>PS1_V4@ source->lensingOBJ->shear->X11 = psMetadataLookupF32 (&status, row, "LENS_X11_SH_OBJ");
+	  @>PS1_V4@ source->lensingOBJ->shear->X12 = psMetadataLookupF32 (&status, row, "LENS_X12_SH_OBJ");
+	  @>PS1_V4@ source->lensingOBJ->shear->X22 = psMetadataLookupF32 (&status, row, "LENS_X22_SH_OBJ");
+	  @>PS1_V4@ source->lensingOBJ->shear->e1  = psMetadataLookupF32 (&status, row, "LENS_E1_SH_OBJ");
+	  @>PS1_V4@ source->lensingOBJ->shear->e2  = psMetadataLookupF32 (&status, row, "LENS_E2_SH_OBJ");
+	}
+
+	if (haveLensPSF) {
+	  source->lensingPSF = pmSourceLensingAlloc ();
+	  source->lensingPSF->smear = pmLensingParsAlloc();
+	  source->lensingPSF->shear = pmLensingParsAlloc();
+
+	  @>PS1_V4@ source->lensingPSF->smear->X11 = psMetadataLookupF32 (&status, row, "LENS_X11_SM_PSF");
+	  @>PS1_V4@ source->lensingPSF->smear->X12 = psMetadataLookupF32 (&status, row, "LENS_X12_SM_PSF");
+	  @>PS1_V4@ source->lensingPSF->smear->X22 = psMetadataLookupF32 (&status, row, "LENS_X22_SM_PSF");
+	  @>PS1_V4@ source->lensingPSF->smear->e1  = psMetadataLookupF32 (&status, row, "LENS_E1_SM_PSF");
+	  @>PS1_V4@ source->lensingPSF->smear->e2  = psMetadataLookupF32 (&status, row, "LENS_E2_SM_PSF");
+	  @>PS1_V4@ source->lensingPSF->shear->X11 = psMetadataLookupF32 (&status, row, "LENS_X11_SH_PSF");
+	  @>PS1_V4@ source->lensingPSF->shear->X12 = psMetadataLookupF32 (&status, row, "LENS_X12_SH_PSF");
+	  @>PS1_V4@ source->lensingPSF->shear->X22 = psMetadataLookupF32 (&status, row, "LENS_X22_SH_PSF");
+	  @>PS1_V4@ source->lensingPSF->shear->e1  = psMetadataLookupF32 (&status, row, "LENS_E1_SH_PSF");
+	  @>PS1_V4@ source->lensingPSF->shear->e2  = psMetadataLookupF32 (&status, row, "LENS_E2_SH_PSF");
+	}
 
         @>PS1_V2,PS1_SV?,>PS1_DV1@ source->moments->Mrf         = psMetadataLookupF32 (&status, row, "MOMENTS_R1");
@@ -448,6 +537,8 @@
     }
 
+#ifdef SORT_EXTENSIONS_BY_FLUX
     // let's write these out in S/N order
     sources = psArraySort (sources, pmSourceSortByFlux);
+#endif
 
     table = psArrayAllocEmpty (sources->n);
@@ -708,21 +799,9 @@
             return false;
         }
-        // Find the source with this sequence number. 
-        // XXX: I am assuming that sources is sorted in order of seq
+        // Find the source with this sequence number using the sourceIndex. 
         long seq = psMetadataLookupU32 (&status, row, "IPP_IDET");
-        pmSource *source = NULL;
-#ifndef ASSUME_SORTED
-        long j = seq < sources->n ? seq : sources->n - 1;
-        for (; j >= 0; j--) {
-            source = sources->data[j];
-            if (source->seq == seq) {
-                break;
-            }
-        }
-#else
         long j = sourceIndex[seq];
         psAssert(j >= 0 && j < sources->n, "invalid sourceIndex");
-        source = sources->data[j];
-#endif
+        pmSource *source = sources->data[j];
         if (!source) {
             psError(PS_ERR_UNKNOWN, false, "Failed to find source for row %ld sequence number %ld\n", i, seq);
@@ -789,18 +868,24 @@
     char name[64];
 
+    pmModelType modelTypeTrail = pmModelClassGetType("PS_MODEL_TRAIL");
+
     // create a header to hold the output data
     psMetadata *outhead = psMetadataAlloc ();
 
+    pmModelClassWriteHeader(outhead);
+
     // write the links to the image header
     psMetadataAddStr (outhead, PS_LIST_TAIL, "EXTNAME", PS_META_REPLACE, "xsrc table extension", extname);
 
+#ifdef SORT_EXTENSIONS_BY_FLUX
     // let's write these out in S/N order
     sources = psArraySort (sources, pmSourceSortByFlux);
-
-    @>PS1_DV2@ float magOffset; 
-    @>PS1_DV2@ float zeroptErr; 
-    @>PS1_DV2@ float fwhmMajor; 
-    @>PS1_DV2@ float fwhmMinor;
-    @>PS1_DV2@ pmSourceOutputsCommonValues (&magOffset, &zeroptErr, &fwhmMajor, &fwhmMinor, readout, imageHeader);
+#endif
+
+    float magOffset; 
+    float zeroptErr; 
+    float fwhmMajor; 
+    float fwhmMinor;
+    pmSourceOutputsCommonValues (&magOffset, &zeroptErr, &fwhmMajor, &fwhmMinor, readout, imageHeader);
 
     // we are writing one row per model; we need to write out same number of columns for each row: find the max Nparams
@@ -823,4 +908,12 @@
     @>PS1_DV2@ pmChip *chip = readout->parent->parent;
 
+    pmModelStatus badModel = PM_MODEL_STATUS_NONE;
+    badModel |= PM_MODEL_STATUS_BADARGS;
+    badModel |= PM_MODEL_STATUS_OFFIMAGE;
+    badModel |= PM_MODEL_STATUS_NAN_CHISQ;
+    badModel |= PM_MODEL_SERSIC_PCM_FAIL_GUESS;
+    badModel |= PM_MODEL_SERSIC_PCM_FAIL_GRID;
+    badModel |= PM_MODEL_PCM_FAIL_GUESS;
+
     table = psArrayAllocEmpty (sources->n);
 
@@ -847,5 +940,6 @@
 
 	    // skip models which were not actually fitted
-	    if (model->flags & PM_MODEL_STATUS_BADARGS) continue;
+	    // XXX 
+	    if (model->flags & badModel) continue;
 
             PAR = model->params->data.F32;
@@ -853,6 +947,21 @@
             xPos = PAR[PM_PAR_XPOS];
             yPos = PAR[PM_PAR_YPOS];
-            xErr = dPAR[PM_PAR_XPOS];
-            yErr = dPAR[PM_PAR_YPOS];
+
+	    // for the extended source models, we do not always fit the centroid in the non-linear fitting process
+	    // current situation (hard-wired into psphotSourceFits.c:psphotFitPCM,
+	    // SERSIC, DEV, EXP : X,Y not fitted (PCM and not PCM)
+	    // TRAIL : X,Y are fitted
+	    // 
+	    
+	    // XXX this should be based on what happened, not on the model type
+	    if (model->type == modelTypeTrail) {
+		xErr = dPAR[PM_PAR_XPOS];
+		yErr = dPAR[PM_PAR_YPOS];
+	    } else {
+		// this is definitely an underestimate since it does not
+		// account for the extent of the source
+		xErr = fwhmMajor * model->magErr / 2.35;
+		yErr = fwhmMinor * model->magErr / 2.35;
+	    }
 
 	    @>PS1_DV2@ psSphere ptSky = {0.0, 0.0, 0.0, 0.0};
@@ -870,5 +979,4 @@
             row = psMetadataAlloc ();
 
-            // XXX we are not writing out the mode (flags) or the type (psf, ext, etc)
 	    // the psMetadataAdd entry and the double quotes are used by grep to select the output fields for automatic documentation
 	    // This set of psMetadataAdd Entries marks the "----" "Start of the XFIT segment"
@@ -888,6 +996,10 @@
 	    @>PS1_DV2@ float calMag = isfinite(magOffset) ? model->mag + magOffset : NAN;
 	    @>PS1_DV2@ psMetadataAdd (row, PS_LIST_TAIL, "EXT_CAL_MAG", PS_DATA_F32, "EXT Magnitude using supplied calibration",   calMag);
-	    @>PS1_DV2@ psMetadataAdd (row, PS_LIST_TAIL, "EXT_CHISQ",   PS_DATA_F32, "EXT Magnitude using supplied calibration",   model->chisq);
-	    @>PS1_DV2@ psMetadataAdd (row, PS_LIST_TAIL, "EXT_NDOF",    PS_DATA_S32, "EXT Magnitude using supplied calibration",   model->nDOF);
+	    @>PS1_DV2,PS1_SV?@ psMetadataAdd (row, PS_LIST_TAIL, "EXT_CHISQ",   PS_DATA_F32, "EXT Model Chisq",   model->chisq);
+	    @>PS1_DV2,PS1_SV?@ psMetadataAdd (row, PS_LIST_TAIL, "EXT_NDOF",    PS_DATA_S32, "EXT Model num degrees of freedom",   model->nDOF);
+	    @>PS1_SV1,PS1_SV?@ psMetadataAdd (row, PS_LIST_TAIL, "EXT_MODEL_TYPE",    PS_DATA_S32, "type for chosen EXT_MODEL",   source->modelEXT ? source->modelEXT->type : -1);
+
+	    // EAM : adding for PV2 outputs:
+	    @>PS1_SV1@ psMetadataAdd (row, PS_LIST_TAIL, "EXT_FLAGS", PS_DATA_S16, "model fit flags (pmModelStatus)", model->flags);
 
             @>PS1_DV2@ psMetadataAddF32 (row, PS_LIST_TAIL, "POSANGLE",   0, "position angle at source (degrees)",         posAngle);
@@ -925,5 +1037,5 @@
 		    psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_THETA_ERR",     0, "EXT angle err (SXY, isnan)", dPAR[PM_PAR_SXY]);
 		} else {
-		    psEllipseAxes axes = pmPSF_ModelToAxes (PAR, model->type);
+		    psEllipseAxes axes = pmPSF_ModelToAxes (PAR, model->class->useReff);
 		    psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_WIDTH_MAJ",    0, "EXT width (major axis), length for trail", axes.major);
 		    psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_WIDTH_MIN",    0, "EXT width (minor axis), sigma for trail",  axes.minor);
@@ -946,5 +1058,5 @@
 
                 snprintf (name, 64, "EXT_PAR_%02d", k);
-
+	       
                 if (k < model->params->n) {
                     psMetadataAddF32 (row, PS_LIST_TAIL, name, 0, "", model->params->data.F32[k]);
@@ -956,5 +1068,5 @@
 	    // optionally, write out the covariance matrix values
 	    // XXX do I need to pad this to match the biggest covar matrix?
-	    if (model->covar) {
+	    if (false && model->covar) {
 		for (int iy = 0; iy < model->covar->numCols; iy++) {
 		    for (int ix = iy; ix < model->covar->numCols; ix++) {
@@ -1005,4 +1117,7 @@
         return false;
     }
+    // set up the lookup table to translate between input model types and output model types
+    // if not defined it is assumed that the tables are the same
+    pmModelClassReadHeader(tableHeader);
 
     for (long i = 0; i < numSources; i++) {
@@ -1013,15 +1128,8 @@
             return false;
         }
-        // Find the source with this sequence number. 
-        // XXX: I am assuming that sources is sorted in order of seq.
         long seq = psMetadataLookupU32 (&status, row, "IPP_IDET");
-        long j = seq < sources->n ? seq : sources->n - 1;
-        pmSource *source = NULL;
-        for (; j >= 0; j--) {
-            source = sources->data[j];
-            if (source->seq == seq) {
-                break;
-            }
-        }
+        long j = sourceIndex[seq];
+        psAssert(j >= 0 && j < sources->n, "invalid sourceIndex");
+        pmSource *source = sources->data[j];
         if (!source) {
             psError(PS_ERR_UNKNOWN, false, "Failed to find source for row %ld sequence number %ld\n", i, seq);
@@ -1058,9 +1166,26 @@
         model->magErr = psMetadataLookupF32(&status, row, "EXT_INST_MAG_SIG");
 
+        model->chisq = psMetadataLookupF32(&status, row, "EXT_CHISQ");
+        model->nDOF = psMetadataLookupF32(&status, row, "EXT_NDOF");
+        model->flags = psMetadataLookupS16(&status, row, "EXT_FLAGS");
+
+        // EXT_MODEL_TYPE gives the model chosen by psphot as the best.
+        // Putting this into the XFIT table makes 3 copies of it (one for each model)
+        // but since we have many fewer XFIT rows than psf rows that is cheaper than putting it
+        // in the psf table.
+        psS32 extModelType = psMetadataLookupS32(&status, row, "EXT_MODEL_TYPE");
+        if (status) {
+            // translate between the type value in xfit and values used by this program
+            extModelType = pmModelClassGetLocalType(extModelType);
+        } else {
+            // older cmfs don't have this column
+            extModelType = -1;
+        } 
+
         psEllipseAxes axes;
         axes.major = psMetadataLookupF32(&status, row, "EXT_WIDTH_MAJ");
         axes.minor = psMetadataLookupF32(&status, row, "EXT_WIDTH_MIN");
         axes.theta = psMetadataLookupF32(&status, row, "EXT_THETA");
-        if (!pmPSF_AxesToModel(PAR, axes, modelType)) {
+        if (!pmPSF_AxesToModel(PAR, axes, model->class->useReff)) {
             // Do we need to fail here or can this happen?
             psError(PS_ERR_UNKNOWN, false, "Failed to convert psf axes to model");
@@ -1072,20 +1197,40 @@
         if (model->params->n > 7) {
             PAR[7] = psMetadataLookupF32(&status, row, "EXT_PAR_07");
-        }
-        // read the covariance matrix
-        int nparams = model->params->n;
-        psImage *covar = psImageAlloc(nparams, nparams, PS_TYPE_F32);
-        for (int y = 0; y < nparams; y++) {
-            for (int x = 0; x < nparams; x++) {
-                char name[64];
-                snprintf(name, 64, "EXT_COVAR_%02d_%02d", y, x);
-                covar->data.F32[y][x] = psMetadataLookupF32(&status, row, name);
+            // XXX add an error:
+	    // dPAR[7] = psMetadataLookupF32(&status, row, "EXT_PAR_07_");
+        }
+
+	// XXX : make this depend on what is in the cmf
+	if (0) {
+	    // read the covariance matrix
+	    int nparams = model->params->n;
+	    psImage *covar = psImageAlloc(nparams, nparams, PS_TYPE_F32);
+	    for (int y = 0; y < nparams; y++) {
+		for (int x = 0; x < nparams; x++) {
+		    char name[64];
+		    snprintf(name, 64, "EXT_COVAR_%02d_%02d", y, x);
+		    covar->data.F32[y][x] = psMetadataLookupF32(&status, row, name);
+		}
+	    }
+	    model->covar = covar;
+	}
+
+        if (modelType == extModelType) {
+            // The software that created this source picked this model as the best of the fits. 
+            // Set the extModel to point to it.
+            // This is important for programs like psastro (skycal) so that its output cmfs
+            // will have valid EXT_MODEL_TYPE
+            psFree(source->modelEXT);
+            source->modelEXT = psMemIncrRefCounter(model);
+            source->type = PM_SOURCE_TYPE_EXTENDED;
+            if (0) {
+                // since FLAGS were read we don't need to do this
+                source->mode |= PM_SOURCE_MODE_EXTMODEL;
+                source->mode |= PM_SOURCE_MODE_NONLINEAR_FIT;
             }
         }
-        model->covar = covar;
 
         psArrayAdd(source->modelFits, 1, model);
         psFree(model);
-
         psFree(row);
     }
@@ -1140,6 +1285,8 @@
     psVector *fwhmValues = psMetadataLookupVector(&status, readout->analysis, "STACK.PSF.FWHM.VALUES");
 
+#ifdef SORT_EXTENSIONS_BY_FLUX
     // let's write these out in S/N order
     sources = psArraySort (sources, pmSourceSortByFlux);
+#endif
 
     table = psArrayAllocEmpty (sources->n);
@@ -1209,8 +1356,8 @@
 
 	write_annuli:
-	    psMetadataAdd (row, PS_LIST_TAIL, "APER_FLUX",     	 PS_DATA_VECTOR, "flux within annuli",       radFlux);
-	    psMetadataAdd (row, PS_LIST_TAIL, "APER_FLUX_ERR", 	 PS_DATA_VECTOR, "flux error in annuli",     radFluxErr);
-	    psMetadataAdd (row, PS_LIST_TAIL, "APER_FLUX_STDEV", PS_DATA_VECTOR, "flux standard deviation",  radFluxStdev);
-	    psMetadataAdd (row, PS_LIST_TAIL, "APER_FILL",       PS_DATA_VECTOR, "fill factor of annuli",    radFill);
+	    psMetadataAddVector (row, PS_LIST_TAIL, "APER_FLUX",       PS_META_REPLACE, "flux within annuli",       radFlux);
+	    psMetadataAddVector (row, PS_LIST_TAIL, "APER_FLUX_ERR",   PS_META_REPLACE, "flux error in annuli",     radFluxErr);
+	    psMetadataAddVector (row, PS_LIST_TAIL, "APER_FLUX_STDEV", PS_META_REPLACE, "flux standard deviation",  radFluxStdev);
+	    psMetadataAddVector (row, PS_LIST_TAIL, "APER_FILL",       PS_META_REPLACE, "fill factor of annuli",    radFill);
 	    psFree (radFlux);
 	    psFree (radFluxErr);
@@ -1277,15 +1424,8 @@
             return false;
         }
-        // Find the source with this sequence number. 
-        // XXX: I am assuming that sources is sorted in order of seq.
         long seq = psMetadataLookupU32 (&status, row, "IPP_IDET");
-        long j = seq < sources->n ? seq : sources->n - 1;
-        pmSource *source = NULL;
-        for (; j >= 0; j--) {
-            source = sources->data[j];
-            if (source->seq == seq) {
-                break;
-            }
-        }
+        long j = sourceIndex[seq];
+        psAssert(j >= 0 && j < sources->n, "invalid sourceIndex");
+        pmSource *source = sources->data[j];
         if (!source) {
             psError(PS_ERR_UNKNOWN, false, "Failed to find source for row %ld sequence number %ld\n", i, seq);
@@ -1343,2 +1483,203 @@
     return true;
 }
+
+// XXX where should I record the number of columns??
+bool pmSourcesWrite_CMF_@CMFMODE@_XGAL (psFits *fits, pmReadout *readout, psArray *sources, char *extname, psMetadata *recipe)
+{
+    bool status = false;
+
+    // perform full non-linear fits / extended source analysis?
+    if (!psMetadataLookupBool (&status, recipe, "GALAXY_SHAPES")) {
+	psLogMsg ("psphot", PS_LOG_INFO, "galaxy shapes were not measured, skipping\n");
+	return true;
+    }
+
+    // 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, "galaxy table extension", extname);
+
+    psF32 Q = psMetadataLookupF32(&status, recipe, "GALAXY_SHAPES_Q");
+    psMetadataAddF32 (outhead, PS_LIST_TAIL, "GALAXY_SHAPES_Q", PS_META_REPLACE, "", Q);
+
+    psF32 NSigma = psMetadataLookupF32(&status, recipe, "GALAXY_SHAPES_NSIGMA");
+    psMetadataAddF32 (outhead, PS_LIST_TAIL, "GALAXY_SHAPES_NSIGMA", PS_META_REPLACE, "", NSigma);
+
+    psF32 clampSN = psMetadataLookupF32(&status, recipe, "GALAXY_SHAPES_CLAMP_SN");
+    psMetadataAddF32 (outhead, PS_LIST_TAIL, "GALAXY_SHAPES_CLAMP_SN", PS_META_REPLACE, "", clampSN);
+
+    // They are probably already in this order but ...
+    sources = psArraySort (sources, pmSourceSortBySeq);
+
+    psArray *table = psArrayAllocEmpty (sources->n);
+
+    for (int i = 0; i < sources->n; i++) {
+
+        pmSource *thisSource = sources->data[i];
+
+	// this is the "real" version of this source 
+	pmSource *source = thisSource->parent ? thisSource->parent : thisSource;
+
+	// if we did not fit the galaxy model, modelFits will be NULL
+        if (source->modelFits == NULL) continue;
+
+	// if we did not fit the galaxy model, galaxyFits will also be NULL
+        if (source->galaxyFits == NULL) continue;
+
+        for (int iModel = 0; iModel < source->modelFits->n; iModel++) {
+            pmModel *model = source->modelFits->data[iModel];
+            if (!model) continue;
+
+            pmSourceGalaxyFits *galaxyFits = NULL;
+            for (int iFit = 0; iFit < source->galaxyFits->n; iFit++) {
+                galaxyFits = source->galaxyFits->data[iFit];
+                if (model->type == galaxyFits->modelType) break;
+                galaxyFits = NULL;
+            }
+
+            if (!galaxyFits) continue;
+
+            // X,Y coordinates are stored with the model parameters
+            psF32 *PAR = model->params->data.F32;
+
+            psMetadata *row = psMetadataAlloc ();
+
+            // we write out the x,y positions so people can link to the psf either way (position or ID)
+            psMetadataAddU32 (row, PS_LIST_TAIL, "IPP_IDET",         0, "IPP detection identifier index", source->seq);
+            psMetadataAddS32 (row, PS_LIST_TAIL, "MODEL_TYPE",       0, "model type",                     galaxyFits->modelType);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "X_FIT",            0, "model x coordinate",             PAR[PM_PAR_XPOS]);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "Y_FIT",            0, "model y coordinate",             PAR[PM_PAR_YPOS]);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "NPIX",             0, "number of pixels for fits",      galaxyFits->nPix);
+            // psMetadataAddS32 (row, PS_LIST_TAIL, "REDUCED_TRIALS",   0, "source has reduced number of trials",      galaxyFits->reducedTrials);
+
+            psVector *Flux = galaxyFits->Flux;
+            psVector *dFlux = galaxyFits->dFlux;
+            psVector *chisq = galaxyFits->chisq;
+
+            psMetadataAddVector (row, PS_LIST_TAIL, "GAL_FLUX",     PS_META_REPLACE, "normalization for galaxy flux", Flux);
+            psMetadataAddVector (row, PS_LIST_TAIL, "GAL_FLUX_ERR", PS_META_REPLACE, "error on normalization", dFlux);
+            psMetadataAddVector (row, PS_LIST_TAIL, "GAL_CHISQ",    PS_META_REPLACE, "galaxy fit chisq", chisq);
+
+            psMetadataAddF32 (row, PS_LIST_TAIL, "FR_MAJOR_MIN",    0, "fractional major axis min",      galaxyFits->fRmajorMin);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "FR_MAJOR_MAX",    0, "fractional major axis max",      galaxyFits->fRmajorMax);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "FR_MAJOR_DEL",    0, "fractional major axis max",      galaxyFits->fRmajorDel);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "FR_MINOR_MIN",    0, "fractional minor axis min",      galaxyFits->fRminorMin);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "FR_MINOR_MAX",    0, "fractional minor axis max",      galaxyFits->fRminorMax);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "FR_MINOR_DEL",    0, "fractional minor axis max",      galaxyFits->fRminorDel);
+
+            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 galaxy data %s\n", extname);
+    if (!psFitsWriteTable (fits, outhead, table, extname)) {
+        psError(psErrorCodeLast(), false, "writing galaxy data %s\n", extname);
+        psFree (outhead);
+        psFree(table);
+        return false;
+    }
+    psFree (outhead);
+    psFree (table);
+    return true;
+}
+
+bool pmSourcesRead_CMF_@CMFMODE@_XGAL(psFits *fits, pmReadout *readout, psMetadata *hduHeader, psMetadata *tableHeader, psArray *sources, long *sourceIndex)
+{
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_PTR_NON_NULL(sources, false);
+
+    bool status;
+    long numSources = psFitsTableSize(fits); // Number of sources in table
+    if (numSources == 0) {
+        psError(psErrorCodeLast(), false, "XGAL Table contains no entries\n");
+        return false;
+    }
+    if (!readout->analysis) {
+        readout->analysis = psMetadataAlloc();
+    }
+    psF32 Q = psMetadataLookupF32(&status, tableHeader, "GALAXY_SHAPES_Q");
+    // XXX: turn this into an assert once we're done
+    if (status) {
+        psMetadataAddF32 (readout->analysis, PS_LIST_TAIL, "GALAXY_SHAPES_Q", PS_META_REPLACE, "", Q);
+
+        psF32 NSigma = psMetadataLookupF32(&status, tableHeader, "GALAXY_SHAPES_NSIGMA");
+        psAssert(status, "missing GALAXY_SHAPES_NSIGMA");
+        psMetadataAddF32 (readout->analysis, PS_LIST_TAIL, "GALAXY_SHAPES_NSIGMA", PS_META_REPLACE, "", NSigma);
+
+        psF32 clampSN = psMetadataLookupF32(&status, tableHeader, "GALAXY_SHAPES_CLAMP_SN");
+        psAssert(status, "missing GALAXY_SHAPES_CLAMP_SN");
+        psMetadataAddF32 (readout->analysis, PS_LIST_TAIL, "GALAXY_SHAPES_CLAMP_SN", PS_META_REPLACE, "", clampSN);
+    }
+
+    for (long i = 0; i < numSources; i++) {
+        psMetadata *row = psFitsReadTableRow(fits, i); // Table row
+        if (!row) {
+            psError(psErrorCodeLast(), false, "Unable to read row %ld of sources", i);
+            psFree(row);
+            return false;
+        }
+        // Find the source with this sequence number. 
+        // XXX: I am assuming that sources is sorted in order of seq
+        long seq = psMetadataLookupU32 (&status, row, "IPP_IDET");
+        long j = sourceIndex[seq];
+        psAssert(j >= 0 && j < sources->n, "invalid sourceIndex");
+
+        pmSource *source = sources->data[j];
+        if (!source) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to find source for row %ld sequence number %ld\n", i, seq);
+            psFree(row);
+            return false;
+        }
+
+        int modelType = psMetadataLookupS32 (&status,    row, "MODEL_TYPE");
+        psVector *Flux  = psMetadataLookupVector(&status, row, "GAL_FLUX");
+        psVector *dFlux = psMetadataLookupVector(&status, row, "GAL_FLUX_ERR");
+        psVector *chisq = psMetadataLookupVector(&status, row, "GAL_CHISQ");
+
+        if (Flux && Flux->n > 0) {
+            if (!source->galaxyFits) {
+                source->galaxyFits = psArrayAllocEmpty(1);
+            }
+
+            pmSourceGalaxyFits *galaxyFits = pmSourceGalaxyFitsAlloc();
+
+            psArrayAdd(source->galaxyFits, 1, galaxyFits);
+
+            psFree(galaxyFits);
+            galaxyFits->modelType = modelType;
+            galaxyFits->nPix = psMetadataLookupF32(&status, row, "NPIX");
+
+            galaxyFits->fRmajorMin = psMetadataLookupF32(&status, row, "FR_MAJOR_MIN");
+            galaxyFits->fRmajorMax = psMetadataLookupF32(&status, row, "FR_MAJOR_MAX");
+            galaxyFits->fRmajorDel = psMetadataLookupF32(&status, row, "FR_MAJOR_DEL");
+            galaxyFits->fRminorMin = psMetadataLookupF32(&status, row, "FR_MINOR_MIN");
+            galaxyFits->fRminorMax = psMetadataLookupF32(&status, row, "FR_MINOR_MAX");
+            galaxyFits->fRminorDel = psMetadataLookupF32(&status, row, "FR_MINOR_DEL");
+
+            psFree(galaxyFits->Flux);
+            galaxyFits->Flux  = psMemIncrRefCounter(Flux);
+            psFree(galaxyFits->dFlux);
+            galaxyFits->dFlux = psMemIncrRefCounter(dFlux);
+            psFree(galaxyFits->chisq);
+            galaxyFits->chisq = psMemIncrRefCounter(chisq);
+        }
+
+        psFree(row);
+    }
+
+    return true;
+}
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_CMP.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_CMP.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_CMP.c	(revision 37066)
@@ -37,11 +37,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -135,5 +136,5 @@
         lsky = (source->sky < 1.0) ? 0.0 : log10(source->sky);
 
-        axes = pmPSF_ModelToAxes (PAR, model->type);
+        axes = pmPSF_ModelToAxes (PAR, model->class->useReff);
 
         float psfMagErr = isfinite(source->psfMagErr) ? source->psfMagErr : 999;
@@ -292,5 +293,5 @@
                 goto skip_source;
 
-            pmPSF_AxesToModel (PAR, axes, modelType);
+            pmPSF_AxesToModel (PAR, axes, source->modelPSF->class->useReff);
 
             psArrayAdd (sources, 100, source);
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_MatchedRefs.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_MatchedRefs.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_MatchedRefs.c	(revision 37066)
@@ -37,11 +37,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -98,22 +99,11 @@
 
                     // select the raw objects for this readout
-                    psArray *rawstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.RAWSTARS");
+                    psArray *rawstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.RAWSTARS.SUBSET");
                     if (rawstars == NULL) continue;
 
                     // select the raw objects for this readout
-                    psArray *refstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.REFSTARS");
+                    psArray *refstars = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.REFSTARS.SUBSET");
                     if (refstars == NULL) continue;
                     psTrace ("psastro", 4, "Trying %ld refstars\n", refstars->n);
-
-# if (0)
-                    // XXX test
-                    FILE *outfile = fopen ("refstars.dat", "w");
-                    assert (outfile);
-                    for (int nn = 0; nn < refstars->n; nn++) {
-                        pmAstromObj *ref = refstars->data[nn];
-                        fprintf (outfile, "%lf %lf\n", ref->sky->r*PS_DEG_RAD, ref->sky->d*PS_DEG_RAD);
-                    }
-                    fclose (outfile);
-# endif
 
                     psArray *matches = psMetadataLookupPtr (NULL, readout->analysis, "PSASTRO.MATCH");
@@ -127,16 +117,18 @@
 
                         psMetadata *row = psMetadataAlloc ();
-                        psMetadataAdd (row, PS_LIST_TAIL, "RA",       PS_DATA_F64, "right ascension (deg, J2000)", PM_DEG_RAD*ref->sky->r);
-                        psMetadataAdd (row, PS_LIST_TAIL, "DEC",      PS_DATA_F64, "declination (deg, J2000)",     PM_DEG_RAD*ref->sky->d);
-                        psMetadataAdd (row, PS_LIST_TAIL, "X_CHIP",   PS_DATA_F32, "x coord on chip",              raw->chip->x);
-                        psMetadataAdd (row, PS_LIST_TAIL, "Y_CHIP",   PS_DATA_F32, "y coord on chip",              raw->chip->y);
-                        psMetadataAdd (row, PS_LIST_TAIL, "X_CHIP_FIT",PS_DATA_F32, "x fitted coord on chip",      ref->chip->x);
-                        psMetadataAdd (row, PS_LIST_TAIL, "Y_CHIP_FIT",PS_DATA_F32, "y fitted coord on chip",      ref->chip->y);
-                        psMetadataAdd (row, PS_LIST_TAIL, "X_FPA",    PS_DATA_F32, "x coord on focal plane",       raw->FP->x);
-                        psMetadataAdd (row, PS_LIST_TAIL, "Y_FPA",    PS_DATA_F32, "y coord on focal plane",       raw->FP->y);
-                        psMetadataAdd (row, PS_LIST_TAIL, "MAG_INST", PS_DATA_F32, "instrumental magnitude",       raw->Mag);
-                        psMetadataAdd (row, PS_LIST_TAIL, "MAG_REF",  PS_DATA_F32, "reference star magnitude",     ref->Mag);
-                        psMetadataAdd (row, PS_LIST_TAIL, "COLOR_REF",PS_DATA_F32, "reference star color",         ref->Color);
-                        psMetadataAdd (row, PS_LIST_TAIL, "CHIP_ID",  PS_DATA_STRING, "chip identifier",           chipName);
+                        psMetadataAdd (row, PS_LIST_TAIL, "RA_REF",     PS_DATA_F64, "right ascension (deg, J2000)", PM_DEG_RAD*ref->sky->r);
+                        psMetadataAdd (row, PS_LIST_TAIL, "DEC_REF",    PS_DATA_F64, "declination (deg, J2000)",     PM_DEG_RAD*ref->sky->d);
+                        psMetadataAdd (row, PS_LIST_TAIL, "X_CHIP_REF", PS_DATA_F32, "x fitted coord on chip",       ref->chip->x);
+                        psMetadataAdd (row, PS_LIST_TAIL, "Y_CHIP_REF", PS_DATA_F32, "y fitted coord on chip",       ref->chip->y);
+                        psMetadataAdd (row, PS_LIST_TAIL, "X_CHIP_RAW", PS_DATA_F32, "x coord on chip",              raw->chip->x);
+                        psMetadataAdd (row, PS_LIST_TAIL, "Y_CHIP_RAW", PS_DATA_F32, "y coord on chip",              raw->chip->y);
+                        psMetadataAdd (row, PS_LIST_TAIL, "X_FPA_RAW",  PS_DATA_F32, "x coord on focal plane",       raw->FP->x);
+                        psMetadataAdd (row, PS_LIST_TAIL, "Y_FPA_RAW",  PS_DATA_F32, "y coord on focal plane",       raw->FP->y);
+                        psMetadataAdd (row, PS_LIST_TAIL, "X_TPA_RAW",  PS_DATA_F32, "x coord on focal plane",       raw->TP->x);
+                        psMetadataAdd (row, PS_LIST_TAIL, "Y_TPA_RAW",  PS_DATA_F32, "y coord on focal plane",       raw->TP->y);
+                        psMetadataAdd (row, PS_LIST_TAIL, "MAG_INST",   PS_DATA_F32, "instrumental magnitude",       raw->Mag);
+                        psMetadataAdd (row, PS_LIST_TAIL, "MAG_REF",    PS_DATA_F32, "reference star magnitude",     ref->Mag);
+                        psMetadataAdd (row, PS_LIST_TAIL, "COLOR_REF",  PS_DATA_F32, "reference star color",         ref->Color);
+                        psMetadataAdd (row, PS_LIST_TAIL, "CHIP_ID",    PS_DATA_STRING, "chip identifier",           chipName);
                         // XXX need to add the reference color, but this needs getstar / dvo.photcodes for the reference to be refined.
 
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_OBJ.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_OBJ.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_OBJ.c	(revision 37066)
@@ -37,11 +37,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -92,5 +93,5 @@
         }
 
-        axes = pmPSF_ModelToAxes (PAR, model->type);
+        axes = pmPSF_ModelToAxes (PAR, model->class->useReff);
 
         psLineInit (line);
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_PS1_CAL_0.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_PS1_CAL_0.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_PS1_CAL_0.c	(revision 37066)
@@ -37,11 +37,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -114,5 +115,5 @@
             yErr = dPAR[PM_PAR_YPOS];
 	    if (isfinite(PAR[PM_PAR_SXX]) && isfinite(PAR[PM_PAR_SXX]) && isfinite(PAR[PM_PAR_SXX])) {
-		axes = pmPSF_ModelToAxes (PAR, model->type);
+		axes = pmPSF_ModelToAxes (PAR, model->class->useReff);
 	    } else {
 		axes.major = NAN;
@@ -289,5 +290,5 @@
 	dPAR[PM_PAR_I0]   = (isfinite(source->psfMag)) ? PAR[PM_PAR_I0] * source->psfMagErr : NAN;
 
-        pmPSF_AxesToModel (PAR, axes, modelType);
+        pmPSF_AxesToModel (PAR, axes, model->class->useReff);
 
         float peakMag     = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
@@ -623,5 +624,5 @@
 	    yErr = dPAR[PM_PAR_YPOS];
 
-	    axes = pmPSF_ModelToAxes (PAR, model->type);
+	    axes = pmPSF_ModelToAxes (PAR, model->class->useReff);
 
 	    // generate RA,DEC
@@ -713,2 +714,7 @@
     return true;
 }
+
+bool pmSourcesWrite_PS1_CAL_0_XGAL (psFits *fits, pmReadout *readout, psArray *sources, char *extname, psMetadata *recipe)
+{
+    return true;
+}
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_PS1_DEV_0.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_PS1_DEV_0.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_PS1_DEV_0.c	(revision 37066)
@@ -37,11 +37,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -90,5 +91,5 @@
             yErr = dPAR[PM_PAR_YPOS];
 
-            axes = pmPSF_ModelToAxes (PAR, model->type);
+            axes = pmPSF_ModelToAxes (PAR, model->class->useReff);
         } else {
             // XXX: This code seg faults if source->peak is NULL.
@@ -215,5 +216,5 @@
         source->psfMagErr    = psMetadataLookupF32 (&status, row, "PSF_INST_MAG_SIG");
 
-        pmPSF_AxesToModel (PAR, axes, modelType);
+        pmPSF_AxesToModel (PAR, axes, model->class->useReff);
 
         float peakMag = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
@@ -255,2 +256,7 @@
     return true;
 }
+
+bool pmSourcesWrite_PS1_DEV_0_XGAL(psFits *fits, pmReadout *readout, psArray *sources, char *extname, psMetadata *recipe)
+{
+    return true;
+}
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_PS1_DEV_1.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_PS1_DEV_1.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_PS1_DEV_1.c	(revision 37066)
@@ -37,11 +37,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -96,5 +97,5 @@
             yErr = dPAR[PM_PAR_YPOS];
 	    if (isfinite(PAR[PM_PAR_SXX]) && isfinite(PAR[PM_PAR_SXX]) && isfinite(PAR[PM_PAR_SXX])) {
-		axes = pmPSF_ModelToAxes (PAR, model->type);
+		axes = pmPSF_ModelToAxes (PAR, model->class->useReff);
 	    } else {
 		axes.major = NAN;
@@ -258,5 +259,5 @@
 	dPAR[PM_PAR_I0]   = (isfinite(source->psfMag)) ? PAR[PM_PAR_I0] * source->psfMagErr : NAN;
 
-        pmPSF_AxesToModel (PAR, axes, modelType);
+        pmPSF_AxesToModel (PAR, axes, model->class->useReff);
 
         float peakMag     = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
@@ -523,5 +524,5 @@
 	    yErr = dPAR[PM_PAR_YPOS];
 
-	    axes = pmPSF_ModelToAxes (PAR, model->type);
+	    axes = pmPSF_ModelToAxes (PAR, model->class->useReff);
 
 	    row = psMetadataAlloc ();
@@ -595,2 +596,7 @@
     return true;
 }
+
+bool pmSourcesWrite_PS1_DEV_1_XGAL(psFits *fits, pmReadout *readout, psArray *sources, char *extname, psMetadata *recipe)
+{
+    return true;
+}
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_RAW.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_RAW.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_RAW.c	(revision 37066)
@@ -37,11 +37,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_SMPDATA.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_SMPDATA.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_SMPDATA.c	(revision 37066)
@@ -37,11 +37,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -92,5 +93,5 @@
 	    lsky = (source->sky < 1.0) ? 0.0 : log10(source->sky);
 
-	    axes = pmPSF_ModelToAxes (PAR, model->type);
+	    axes = pmPSF_ModelToAxes (PAR, model->class->useReff);
 
 	} else {
@@ -189,5 +190,5 @@
         axes.theta       = psMetadataLookupF32 (&status, row, "THETA");
 
-	pmPSF_AxesToModel (PAR, axes, modelType);
+	pmPSF_AxesToModel (PAR, axes, model->class->useReff);
 
         source->psfMag = psMetadataLookupF32 (&status, row, "MAG_RAW") - ZERO_POINT;
@@ -225,2 +226,7 @@
     return true;
 } 
+
+bool pmSourcesWrite_SMPDATA_XGAL(psFits *fits, pmReadout *readout, psArray *sources, char *extname, psMetadata *recipe)
+{
+    return true;
+} 
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_SX.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_SX.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceIO_SX.c	(revision 37066)
@@ -37,11 +37,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -81,5 +82,5 @@
         // pmSourceSextractType (source, &type, &flags);
 
-        axes = pmPSF_ModelToAxes (PAR, model->type);
+        axes = pmPSF_ModelToAxes (PAR, model->class->useReff);
 
         psLineInit (line);
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceLensing.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceLensing.c	(revision 37066)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceLensing.c	(revision 37066)
@@ -0,0 +1,112 @@
+/** @file  pmSourceLensing.c
+ *
+ *  Functions to measure the local sky and sky variance for sources on images
+ *  @author EAM, IfA: 
+ *  @date $Date: 2014-03-20 $
+ *
+ *  Copyright 2014 Ifa, University of Hawaii
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmMoments.h"
+#include "pmSourceLensing.h"
+
+static void pmLensingParsFree (pmLensingPars *pars) {
+  if (!pars) return;
+  return;
+}
+
+pmLensingPars *pmLensingParsAlloc () {
+
+  pmLensingPars *tmp = (pmLensingPars *) psAlloc(sizeof(pmLensingPars));;
+  psMemSetDeallocator(tmp, (psFreeFunc) pmLensingParsFree);
+
+  tmp->X11 = NAN;
+  tmp->X12 = NAN;
+  tmp->X22 = NAN;
+
+  tmp->e1 = NAN;
+  tmp->e2 = NAN;
+
+  return tmp;
+}
+
+static void pmSourceLensingFree (pmSourceLensing *lensing) {
+  if (!lensing) return;
+  psFree (lensing->smear);
+  psFree (lensing->shear);
+  return;
+}
+
+pmSourceLensing *pmSourceLensingAlloc () {
+
+  pmSourceLensing *tmp = (pmSourceLensing *) psAlloc(sizeof(pmSourceLensing));;
+  psMemSetDeallocator(tmp, (psFreeFunc) pmSourceLensingFree);
+
+  tmp->smear = NULL;
+  tmp->shear = NULL;
+
+  return tmp;
+}
+
+// need to supply the moments and the window-function sigma
+bool pmSourceLensingShearFromMoments (pmSourceLensing *lensing, pmMoments *moments, float sigma) {
+  
+  if (!lensing->shear) {
+    lensing->shear = pmLensingParsAlloc();
+  }
+  
+  pmLensingPars *shear = lensing->shear;
+
+  float R = 1.0 / (moments->Mxx + moments->Myy);
+  float s2 = 1.0 / PS_SQR(sigma);
+  // NOTE : not used by shear : float s4 = PS_SQR(s2);
+
+  shear->X11 = R*(2.0*(moments->Mxx + moments->Myy) - s2 * (moments->Mxxxx - 2.0*moments->Mxxyy + moments->Myyyy));
+
+  shear->X22 = R*(2.0*(moments->Mxx + moments->Myy) - s2 * 4.0 * moments->Mxxyy);
+
+  shear->X12 = R*2.0*s2*(moments->Mxyyy - moments->Mxxxy);
+
+  shear->e1  = R*(2.0*(moments->Mxx - moments->Myy) + s2 * (moments->Myyyy - moments->Mxxxx));
+
+  shear->e2  = R*(4.0*moments->Mxy - 2.0*s2*(moments->Mxxxy + moments->Mxyyy));
+  
+  return true;
+}
+
+// need to supply the moments and the window-function sigma
+// NOTE: I'm using the coefficients from Hoekstra et al 1998, not KSB96
+bool pmSourceLensingSmearFromMoments (pmSourceLensing *lensing, pmMoments *moments, float sigma) {
+  
+  if (!lensing->smear) {
+    lensing->smear = pmLensingParsAlloc();
+  }
+  
+  pmLensingPars *smear = lensing->smear;
+
+  float R = 1.0 / (moments->Mxx + moments->Myy);
+  float s2 = 1.0 / PS_SQR(sigma);
+  float s4 = PS_SQR(s2);
+
+  smear->X11 = R*(1.0 - s2*(moments->Mxx + moments->Myy) + 0.25*s4 * (moments->Mxxxx - 2.0*moments->Mxxyy + moments->Myyyy));
+
+  smear->X22 = R*(1.0 - s2*(moments->Mxx + moments->Myy) + 1.00*s4 * (moments->Mxxyy));
+
+  smear->X12 = R*0.5*s4*(moments->Mxxxy - moments->Mxyyy);
+
+  smear->e1  = R*(s2*(moments->Myy - moments->Mxx) + 0.25*s4 * (moments->Myyyy - moments->Mxxxx));
+
+  smear->e2  = R*(0.5*s4*(moments->Mxxxy + moments->Mxyyy) - 2.0*s2*moments->Mxy);
+  
+  return true;
+}
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceLensing.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceLensing.h	(revision 37066)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceLensing.h	(revision 37066)
@@ -0,0 +1,36 @@
+/* @file  pmSourceLensing.h
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2014-03-20 02:31:25 $
+ * Copyright 2014 IfA, University of Hawaii
+ */
+
+# ifndef PM_SOURCE_LENSING_H
+# define PM_SOURCE_LENSING_H
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+typedef struct {
+  float X11;
+  float X12;
+  float X22;
+  float e1;
+  float e2;
+} pmLensingPars;
+
+typedef struct {
+  pmLensingPars *smear;
+  pmLensingPars *shear;
+} pmSourceLensing; 
+
+pmLensingPars *pmLensingParsAlloc ();
+pmSourceLensing *pmSourceLensingAlloc ();
+
+bool pmSourceLensingShearFromMoments (pmSourceLensing *lensing, pmMoments *moments, float sigma);
+bool pmSourceLensingSmearFromMoments (pmSourceLensing *lensing, pmMoments *moments, float sigma);
+
+/// @}
+# endif /* PM_SOURCE_LENSING_H */
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceMasks.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceMasks.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceMasks.h	(revision 37066)
@@ -56,4 +56,19 @@
     PM_SOURCE_MODE2_DIFF_SELF_MATCH  	  = 0x00000800, ///< positive detection match is probably this source 
     PM_SOURCE_MODE2_SATSTAR_PROFILE       = 0x00001000, ///< saturated source is modeled with a radial profile
+
+    PM_SOURCE_MODE2_ECONTOUR_FEW_PTS      = 0x00002000, ///< too few points to measure the elliptical contour
+    PM_SOURCE_MODE2_RADBIN_NAN_CENTER     = 0x00004000, ///< radial bins failed with too many NaN center bin
+    PM_SOURCE_MODE2_PETRO_NAN_CENTER      = 0x00008000, ///< petrosian radial bins failed with too many NaN center bin
+    PM_SOURCE_MODE2_PETRO_NO_PROFILE      = 0x00010000, ///< petrosian not build because radial bins missing
+
+    PM_SOURCE_MODE2_PETRO_INSIG_RATIO     = 0x00020000, ///< insignificant measurement of petrosian ratio
+    PM_SOURCE_MODE2_PETRO_RATIO_ZEROBIN   = 0x00040000, ///< petrosian ratio in the 0th bin (likely bad)
+    
+    PM_SOURCE_MODE2_EXT_FITS_RUN          = 0x00080000, ///< we attempted to run extended fits on this source
+    PM_SOURCE_MODE2_EXT_FITS_FAIL         = 0x00100000, ///< at least one of the model fits failed
+    PM_SOURCE_MODE2_EXT_FITS_RETRY        = 0x00200000, ///< one of the model fits was re-tried with new window
+    PM_SOURCE_MODE2_EXT_FITS_NONE         = 0x00400000, ///< ALL of the model fits failed
+    
+    
 } pmSourceMode2;
 
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceMatch.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceMatch.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceMatch.c	(revision 37066)
@@ -18,11 +18,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceMoments.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceMoments.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceMoments.c	(revision 37066)
@@ -35,11 +35,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 
@@ -65,6 +66,9 @@
 void pmSourceMomentsSetVerbose(bool state){ beVerbose = state; }
 
+bool pmSourceMomentsHighOrder    (pmSource *source, float radius, float sigma, float minSN, psImageMaskType maskVal);
+bool pmSourceMomentsRadialMoment (pmSource *source, float radius, float minKronRadius, psImageMaskType maskVal);
+bool pmSourceMomentsKronFluxes   (pmSource *source, float sigma,  float minSN, psImageMaskType maskVal);
+
 // if mode & EXTERNAL or mode2 & MATCHED, do not re-calculate the centroid (use peak as centroid)
-
 bool pmSourceMoments(pmSource *source, float radius, float sigma, float minSN, float minKronRadius, psImageMaskType maskVal)
 {
@@ -74,18 +78,7 @@
     PS_ASSERT_FLOAT_LARGER_THAN(radius, 0.0, false);
 
-    // this function assumes the sky has been well-subtracted for the image
-    float sky = 0.0;
-
     if (source->moments == NULL) {
       source->moments = pmMomentsAlloc();
     }
-
-    float Sum = 0.0;
-    float Var = 0.0;
-    float SumCore = 0.0;
-    float VarCore = 0.0;
-    float R2 = PS_SQR(radius);
-    float minSN2 = PS_SQR(minSN);
-    float rsigma2 = 0.5 / PS_SQR(sigma);
 
     // a note about coordinates: coordinates of objects throughout psphot refer to the primary
@@ -110,10 +103,229 @@
     // of any object drops pretty quickly outside 1-2 sigmas.  (The exception is bright
     // saturated stars, for which we need to use a very large radius here)
+    // NOTE: if (mode & EXTERNAL) or (mode2 & MATCHED), do not re-calculate the centroid (use peak as centroid)
+    // (we still call this function because it sets moments->Sum,SN,Peak,nPixels
     if (!pmSourceMomentsGetCentroid (source, 1.5*sigma, 0.0, minSN, maskVal, source->peak->xf, source->peak->yf)) {
 	return false;
     }
 
+    pmSourceMomentsHighOrder (source, radius, sigma, minSN, maskVal);
+
+    // now calculate the 1st radial moment (for kron flux) using symmetrical averaging
+    pmSourceMomentsRadialMoment (source, radius, minKronRadius, maskVal);
+
+    // now calculate the kron flux values using the 1st radial moment
+    pmSourceMomentsKronFluxes (source, sigma, minSN, maskVal);
+
+    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,
+	     source->moments->Mxxxx, source->moments->Mxxxy, source->moments->Mxxyy, source->moments->Mxyyy, source->moments->Myyyy);
+
+    psTrace ("psModules.objects", 3, "peak %f %f (%f = %f) Mx: %f  My: %f  Sum: %f  Mxx: %f  Mxy: %f  Myy: %f  Npix: %d\n",
+	     source->peak->xf, source->peak->yf, 
+	     source->peak->rawFlux, sqrt(source->peak->detValue), 
+	     source->moments->Mx, source->moments->My, 
+	     source->moments->Sum, 
+	     source->moments->Mxx, source->moments->Mxy, source->moments->Myy, 
+	     source->moments->nPixels);
+
+    return(true);
+}
+
+bool pmSourceMomentsGetCentroid(pmSource *source, float radius, float sigma, float minSN, psImageMaskType maskVal, float xGuess, float yGuess) { 
+
+    // First Pass: calculate the first moments (these are subtracted from the coordinates below)
+    // Sum = SUM (z - sky)
+    // X1  = SUM (x - xc)*(z - sky)
+    // .. etc
+
+    float sky = 0.0;
+
+    float peakPixel = -PS_MAX_F32;
+    psS32 numPixels = 0;
+    float Sum = 0.0;
+    float Var = 0.0;
+    float X1 = 0.0;
+    float Y1 = 0.0;
+    float R2 = PS_SQR(radius);
+    float minSN2 = PS_SQR(minSN);
+    float rsigma2 = 0.5 / PS_SQR(sigma);
+
+    float xPeak = xGuess - source->pixels->col0; // coord of peak in subimage
+    float yPeak = yGuess - source->pixels->row0; // coord of peak in subimage
+
+    // we are guaranteed to have a valid pixel and variance at this location (right? right?)
+    // float weightNorm = source->pixels->data.F32[yPeak][xPeak] / sqrt (source->variance->data.F32[yPeak][xPeak]);
+    // psAssert (isfinite(source->pixels->data.F32[yPeak][xPeak]), "peak must be on valid pixel");
+    // psAssert (isfinite(source->variance->data.F32[yPeak][xPeak]), "peak must be on valid pixel");
+    // psAssert (source->variance->data.F32[yPeak][xPeak] > 0, "peak must be on valid pixel");
+
+    // the moments [Sum(x*f) / Sum(f)] are calculated in pixel index values, and should
+    // not depend on the fractional pixel location of the source.  However, the aperture
+    // (radius) and the Gaussian window (sigma) depend subtly on the fractional pixel
+    // position of the expected centroid
+
+    for (psS32 row = 0; row < source->pixels->numRows ; row++) {
+
+	float yDiff = row + 0.5 - yPeak;
+	if (fabs(yDiff) > radius) continue;
+
+	float *vPix = source->pixels->data.F32[row];
+	float *vWgt = source->variance ? source->variance->data.F32[row] : source->pixels->data.F32[row];
+
+	psImageMaskType *vMsk = (source->maskObj == NULL) ? NULL : source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[row];
+	// psImageMaskType *vMsk = (source->maskView == NULL) ? NULL : source->maskView->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;
+
+	    float xDiff = col + 0.5 - xPeak;
+	    if (fabs(xDiff) > radius) continue;
+
+	    // radius is just a function of (xDiff, yDiff)
+	    float r2  = PS_SQR(xDiff) + PS_SQR(yDiff);
+	    if (r2 > R2) continue;
+
+	    float pDiff = *vPix - sky;
+	    float wDiff = *vWgt;
+
+	    // 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
+	    // weighting over weights the sky for faint sources
+	    if (sigma > 0.0) {
+		float z  = r2*rsigma2;
+		assert (z >= 0.0);
+		float weight  = exp(-z);
+
+		wDiff *= weight;
+		pDiff *= weight;
+	    } 
+
+	    Var += wDiff;
+	    Sum += pDiff;
+
+	    float xWght = xDiff * pDiff;
+	    float yWght = yDiff * pDiff;
+
+	    X1  += xWght;
+	    Y1  += yWght;
+
+	    peakPixel = PS_MAX (*vPix, peakPixel);
+	    numPixels++;
+	}
+    }
+
+    // if we have less than (1/4) of the possible pixels (in circle or box), force a retry
+    int minPixels = PS_MIN(0.75*R2, source->pixels->numCols*source->pixels->numRows/4.0);
+
+    // XXX EAM - the limit is a bit arbitrary.  make it user defined?
+    if ((numPixels < minPixels) || (Sum <= 0)) {
+	psTrace ("psModules.objects", 3, "insufficient valid pixels (%d vs %d; %f) for source\n", numPixels, minPixels, Sum);
+	return (false);
+    }
+
+    // calculate the first moment.
+    float Mx = X1/Sum;
+    float My = Y1/Sum;
+    if ((fabs(Mx) > radius) || (fabs(My) > radius)) {
+	psTrace ("psModules.objects", 3, "extreme centroid swing; invalid peak %d, %d\n", source->peak->x, source->peak->y);
+	return (false);
+    }
+    if ((fabs(Mx) > 2.0) || (fabs(My) > 2.0)) {
+	psTrace ("psModules.objects", 3, " big centroid swing; ok peak? %d, %d\n", source->peak->x, source->peak->y);
+    }
+
+    psTrace ("psModules.objects", 5, "id: %d, sky: %f  Mx: %f  My: %f  Sum: %f  X1: %f  Y1: %f  Npix: %d\n", source->id, sky, Mx, My, Sum, X1, Y1, numPixels);
+
+    // add back offset of peak in primary image
+    // also offset from pixel index to pixel coordinate
+    // (the calculation above uses pixel index instead of coordinate)
+    // 0.5 PIX: moments are calculated using the pixel index and converted here to pixel coords
+
+    // we only update the centroid if the position is not supplied from elsewhere
+    bool skipCentroid = false;
+    skipCentroid |= (source->mode  & PM_SOURCE_MODE_EXTERNAL); // skip externally supplied positions
+    skipCentroid |= (source->mode2 & PM_SOURCE_MODE2_MATCHED); // skip sources defined by other image positions
+
+    if (skipCentroid) {
+	source->moments->Mx = source->peak->xf;
+	source->moments->My = source->peak->yf;
+    } else {
+	source->moments->Mx = Mx + xGuess;
+	source->moments->My = My + yGuess;
+    }
+
+    source->moments->Sum = Sum;
+    source->moments->SN  = Sum / sqrt(Var);
+    source->moments->Peak = peakPixel;
+    source->moments->nPixels = numPixels;
+
+    return true;
+}
+
+float pmSourceMinKronRadius(psArray *sources, float PSF_SN_LIM) {
+
+    psVector *radii = psVectorAllocEmpty(100, PS_TYPE_F32);
+
+    for (int i = 0; i < sources->n; i++) {
+	pmSource *src = sources->data[i]; // Source of interest
+	if (!src || !src->moments) {
+	    continue;
+	}
+
+	if (src->mode & PM_SOURCE_MODE_BLEND) {
+	    continue;
+	}
+
+	if (!src->moments->nPixels) continue;
+
+	if (src->moments->SN < PSF_SN_LIM) continue;
+
+	// XXX put in Mxx,Myy cut based on clump location
+
+	psVectorAppend(radii, src->moments->Mrf);
+    }
+
+    // find the peak in this image
+    psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
+
+    if (!psVectorStats (stats, radii, NULL, NULL, 0)) {
+	psError(PS_ERR_UNKNOWN, false, "Unable to get image statistics.\n");
+	psFree(stats);
+	return NAN;
+    }
+
+    float minRadius = stats->sampleMedian;
+
+    psFree(radii);
+    psFree(stats);
+    return minRadius;
+}
+
+bool pmSourceMomentsHighOrder (pmSource *source, float radius, float sigma, float minSN, psImageMaskType maskVal) {
+
+    // this function assumes the sky has been well-subtracted for the image
+    float Sum = 0.0;
+    float R2 = PS_SQR(radius);
+    float minSN2 = PS_SQR(minSN);
+    float rsigma2 = 0.5 / PS_SQR(sigma);
+
     // Now calculate higher-order moments, using the above-calculated first moments to adjust coordinates
-    // Xn  = SUM (x - xc)^n * (z - sky)
+    // Xn  = SUM (x - xc)^n * (z - sky) -- note that sky is 0.0 by definition here
     float XX = 0.0;
     float XY = 0.0;
@@ -129,11 +341,4 @@
     float YYYY = 0.0;
 
-    Sum = 0.0;  // the second pass may include slightly different pixels, re-determine Sum
-
-    // float dX = source->moments->Mx - source->peak->xf;
-    // float dY = source->moments->My - source->peak->yf;
-    // float dR = hypot(dX, dY);
-    // float Xo = (dR < 2.0) ? source->moments->Mx : source->peak->xf;
-    // float Yo = (dR < 2.0) ? source->moments->My : source->peak->yf;
     float Xo = source->moments->Mx;
     float Yo = source->moments->My;
@@ -154,5 +359,4 @@
 
 	psImageMaskType  *vMsk = (source->maskObj == NULL) ? NULL : source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[row];
-	// psImageMaskType  *vMsk = (source->maskView == NULL) ? NULL : source->maskView->data.PS_TYPE_IMAGE_MASK_DATA[row];
 
 	for (psS32 col = 0; col < source->pixels->numCols ; col++, vPix++, vWgt++) {
@@ -173,5 +377,5 @@
 	    if (r2 > R2) continue;
 
-	    float fDiff = *vPix - sky;
+	    float fDiff = *vPix;
 	    float pDiff = fDiff;
 	    float wDiff = *vWgt;
@@ -181,8 +385,8 @@
 	    // stars.
 	    if (PS_SQR(pDiff) < minSN2*wDiff) continue;
-	    if ((minSN > 0.0) && (pDiff < 0)) continue; // 
+	    if ((minSN > 0.0) && (pDiff < 0)) continue; 
 
 	    // Apply a Gaussian window function.  Be careful with the window function.  S/N
-	    // weighting over weights the sky for faint sources
+	    // weighting over-weights the sky for faint sources
 	    if (sigma > 0.0) {
 		float z = r2 * rsigma2;
@@ -230,22 +434,8 @@
 	    XYYY  += xyyy;
 	    YYYY  += yyyy;
-
-	    // Kron Flux uses the 1st radial moment (NOT Gaussian windowed?)
-	    // XXX float r = sqrt(r2);
-	    // XXX float rf = r * fDiff;
-	    // XXX float rh = sqrt(r) * fDiff;
-	    // XXX float rs = fDiff;
-	    // XXX 
-	    // XXX float rfw = r * pDiff;
-	    // XXX float rhw = sqrt(r) * pDiff;
-	    // XXX 
-	    // XXX RF  += rf;
-	    // XXX RH  += rh;
-	    // XXX RS  += rs;
-	    // XXX 
-	    // XXX RFW  += rfw;
-	    // XXX RHW  += rhw;
 	}
     }
+    // NOT needed : source->moments->wSum = Sum;
+
     source->moments->Mxx = XX/Sum;
     source->moments->Mxy = XY/Sum;
@@ -263,8 +453,11 @@
     source->moments->Myyyy = YYYY/Sum;
 
-    // *** now calculate the 1st radial moment (for kron flux) -- symmetrical averaging
+    return true;
+}
+
+bool pmSourceMomentsRadialMoment (pmSource *source, float radius, float minKronRadius, psImageMaskType maskVal) {
+
 
     float **vPix = source->pixels->data.F32;
-    float **vWgt = source->variance ? source->variance->data.F32 : source->pixels->data.F32;
     psImageMaskType **vMsk = (source->maskObj == NULL) ? NULL : source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA;
 
@@ -272,4 +465,15 @@
     float RH = 0.0;
     float RS = 0.0;
+
+    // centroid around which to calculate the moments
+    float Xo = source->moments->Mx;
+    float Yo = source->moments->My;
+
+    // center of mass in subimage.  Note: the calculation below uses pixel index, so we correct
+    // xCM, yCM from pixel coords to pixel index here.
+    float xCM = Xo - 0.5 - source->pixels->col0; // coord of peak in subimage
+    float yCM = Yo - 0.5 - source->pixels->row0; // coord of peak in subimage
+
+    float R2 = PS_SQR(radius);
 
     for (psS32 row = 0; row < source->pixels->numRows ; row++) {
@@ -304,6 +508,6 @@
 	    if (r2 > R2) continue;
 
-	    float fDiff1 = vPix[row][col] - sky;
-	    float fDiff2 = vPix[yFlip][xFlip] - sky;
+	    float fDiff1 = vPix[row][col];
+	    float fDiff2 = vPix[yFlip][xFlip];
 	    float pDiff = (fDiff1 > 0.0) ? sqrt(fabs(fDiff1*fDiff2)) : -sqrt(fabs(fDiff1*fDiff2));
 
@@ -329,11 +533,22 @@
 	kronRefRadius = MIN(radius, kronRefRadius);
     }
-    source->moments->Mrf = kronRefRadius;
-
-    // *** now calculate the kron flux values using the 1st radial moment
-
-    float radKinner = 1.0*kronRefRadius;
-    float radKron   = 2.5*kronRefRadius;
-    float radKouter = 4.0*kronRefRadius;
+
+    // if source is externally supplied and it already has a finite Mrf do not change it
+    if (! ((source->mode & PM_SOURCE_MODE_EXTERNAL) && isfinite(source->moments->Mrf))) {
+        source->moments->Mrf = kronRefRadius;
+    }
+
+    return true;
+}
+
+bool pmSourceMomentsKronFluxes (pmSource *source, float sigma, float minSN, psImageMaskType maskVal) {
+
+    float **vPix = source->pixels->data.F32;
+    float **vWgt = source->variance ? source->variance->data.F32 : source->pixels->data.F32;
+    psImageMaskType **vMsk = (source->maskObj == NULL) ? NULL : source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA;
+
+    float radKinner = 1.0*source->moments->Mrf;
+    float radKron   = 2.5*source->moments->Mrf;
+    float radKouter = 4.0*source->moments->Mrf;
 
     int nKronPix = 0;
@@ -341,7 +556,22 @@
     int nInner = 0;
     int nOuter = 0;
-    Sum = Var = 0.0;
+    
+    float Sum = 0.0;
+    float Var = 0.0;
+    float SumCore = 0.0;
+    float VarCore = 0.0;
     float SumInner = 0.0;
     float SumOuter = 0.0;
+
+    // centroid around which to calculate the moments
+    float Xo = source->moments->Mx;
+    float Yo = source->moments->My;
+
+    // center of mass in subimage.  Note: the calculation below uses pixel index, so we correct
+    // xCM, yCM from pixel coords to pixel index here.
+    float xCM = Xo - 0.5 - source->pixels->col0; // coord of peak in subimage
+    float yCM = Yo - 0.5 - source->pixels->row0; // coord of peak in subimage
+
+    float minSN2 = PS_SQR(minSN);
 
     // calculate the Kron flux, and related fluxes (NO symmetrical averaging)
@@ -362,5 +592,5 @@
 	    float r2  = PS_SQR(xDiff) + PS_SQR(yDiff);
 
-	    float fDiff1 = vPix[row][col] - sky;
+	    float fDiff1 = vPix[row][col];
 	    float pDiff = fDiff1;
 	    float wDiff = vWgt[row][col];
@@ -376,5 +606,4 @@
 		Var += wDiff;
 		nKronPix ++;
-		// if (beVerbose) fprintf (stderr, "mome: %d %d  %f  %f  %f\n", col, row, sky, *vPix, Sum);
 	    }
 
@@ -397,10 +626,10 @@
     }
     // *** should I rescale these fluxes by pi R^2 / nNpix?
-    // XXX source->moments->KronCore    = SumCore       * M_PI * PS_SQR(sigma) / nCorePix;
-    // XXX source->moments->KronCoreErr = sqrt(VarCore) * M_PI * PS_SQR(sigma) / nCorePix;
-    // XXX source->moments->KronFlux    = Sum       * M_PI * PS_SQR(radKron) / nKronPix;
-    // XXX source->moments->KronFluxErr = sqrt(Var) * M_PI * PS_SQR(radKron) / nKronPix;
-    // XXX source->moments->KronFinner = SumInner * M_PI * (PS_SQR(radKron)   - PS_SQR(radKinner)) / nInner;
-    // XXX source->moments->KronFouter = SumOuter * M_PI * (PS_SQR(radKouter) -   PS_SQR(radKron)) / nOuter;
+    // XXX source->moments->KronCore    = SumCore       * M_PI *  PS_SQR(sigma) / nCorePix;
+    // XXX source->moments->KronCoreErr = sqrt(VarCore) * M_PI *  PS_SQR(sigma) / nCorePix;
+    // XXX source->moments->KronFlux    = Sum           * M_PI *  PS_SQR(radKron) / nKronPix;
+    // XXX source->moments->KronFluxErr = sqrt(Var)     * M_PI *  PS_SQR(radKron) / nKronPix;
+    // XXX source->moments->KronFinner  = SumInner      * M_PI * (PS_SQR(radKron)   - PS_SQR(radKinner)) / nInner;
+    // XXX source->moments->KronFouter  = SumOuter      * M_PI * (PS_SQR(radKouter) -   PS_SQR(radKron)) / nOuter;
 
     source->moments->KronCore    = SumCore;
@@ -408,6 +637,6 @@
     source->moments->KronFlux    = Sum;
     source->moments->KronFluxErr = sqrt(Var);
-    source->moments->KronFinner = SumInner;
-    source->moments->KronFouter = SumOuter;
+    source->moments->KronFinner  = SumInner;
+    source->moments->KronFouter  = SumOuter;
 
     // XXX not sure I should save this here...
@@ -416,198 +645,4 @@
     source->moments->KronRadiusPSF  = source->moments->Mrf;
 
-    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,
-	     source->moments->Mxxxx, source->moments->Mxxxy, source->moments->Mxxyy, source->moments->Mxyyy, source->moments->Myyyy);
-
-    psTrace ("psModules.objects", 3, "peak %f %f (%f = %f) Mx: %f  My: %f  Sum: %f  Mxx: %f  Mxy: %f  Myy: %f  sky: %f  Npix: %d\n",
-	     source->peak->xf, source->peak->yf, source->peak->rawFlux, sqrt(source->peak->detValue), source->moments->Mx,   source->moments->My, Sum, source->moments->Mxx,   source->moments->Mxy,   source->moments->Myy, sky, source->moments->nPixels);
-
-    return(true);
-}
-
-bool pmSourceMomentsGetCentroid(pmSource *source, float radius, float sigma, float minSN, psImageMaskType maskVal, float xGuess, float yGuess) { 
-
-    // First Pass: calculate the first moments (these are subtracted from the coordinates below)
-    // Sum = SUM (z - sky)
-    // X1  = SUM (x - xc)*(z - sky)
-    // .. etc
-
-    float sky = 0.0;
-
-    float peakPixel = -PS_MAX_F32;
-    psS32 numPixels = 0;
-    float Sum = 0.0;
-    float Var = 0.0;
-    float X1 = 0.0;
-    float Y1 = 0.0;
-    float R2 = PS_SQR(radius);
-    float minSN2 = PS_SQR(minSN);
-    float rsigma2 = 0.5 / PS_SQR(sigma);
-
-    float xPeak = xGuess - source->pixels->col0; // coord of peak in subimage
-    float yPeak = yGuess - source->pixels->row0; // coord of peak in subimage
-
-    // we are guaranteed to have a valid pixel and variance at this location (right? right?)
-    // float weightNorm = source->pixels->data.F32[yPeak][xPeak] / sqrt (source->variance->data.F32[yPeak][xPeak]);
-    // psAssert (isfinite(source->pixels->data.F32[yPeak][xPeak]), "peak must be on valid pixel");
-    // psAssert (isfinite(source->variance->data.F32[yPeak][xPeak]), "peak must be on valid pixel");
-    // psAssert (source->variance->data.F32[yPeak][xPeak] > 0, "peak must be on valid pixel");
-
-    // the moments [Sum(x*f) / Sum(f)] are calculated in pixel index values, and should
-    // not depend on the fractional pixel location of the source.  However, the aperture
-    // (radius) and the Gaussian window (sigma) depend subtly on the fractional pixel
-    // position of the expected centroid
-
-    for (psS32 row = 0; row < source->pixels->numRows ; row++) {
-
-	float yDiff = row + 0.5 - yPeak;
-	if (fabs(yDiff) > radius) continue;
-
-	float *vPix = source->pixels->data.F32[row];
-	float *vWgt = source->variance ? source->variance->data.F32[row] : source->pixels->data.F32[row];
-
-	psImageMaskType *vMsk = (source->maskObj == NULL) ? NULL : source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[row];
-	// psImageMaskType *vMsk = (source->maskView == NULL) ? NULL : source->maskView->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;
-
-	    float xDiff = col + 0.5 - xPeak;
-	    if (fabs(xDiff) > radius) continue;
-
-	    // radius is just a function of (xDiff, yDiff)
-	    float r2  = PS_SQR(xDiff) + PS_SQR(yDiff);
-	    if (r2 > R2) continue;
-
-	    float pDiff = *vPix - sky;
-	    float wDiff = *vWgt;
-
-	    // 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
-	    // weighting over weights the sky for faint sources
-	    if (sigma > 0.0) {
-		float z  = r2*rsigma2;
-		assert (z >= 0.0);
-		float weight  = exp(-z);
-
-		wDiff *= weight;
-		pDiff *= weight;
-	    } 
-
-	    Var += wDiff;
-	    Sum += pDiff;
-
-	    float xWght = xDiff * pDiff;
-	    float yWght = yDiff * pDiff;
-
-	    X1  += xWght;
-	    Y1  += yWght;
-
-	    peakPixel = PS_MAX (*vPix, peakPixel);
-	    numPixels++;
-	}
-    }
-
-    // if we have less than (1/4) of the possible pixels (in circle or box), force a retry
-    int minPixels = PS_MIN(0.75*R2, source->pixels->numCols*source->pixels->numRows/4.0);
-
-    // XXX EAM - the limit is a bit arbitrary.  make it user defined?
-    if ((numPixels < minPixels) || (Sum <= 0)) {
-	psTrace ("psModules.objects", 3, "insufficient valid pixels (%d vs %d; %f) for source\n", numPixels, minPixels, Sum);
-	return (false);
-    }
-
-    // calculate the first moment.
-    float Mx = X1/Sum;
-    float My = Y1/Sum;
-    if ((fabs(Mx) > radius) || (fabs(My) > radius)) {
-	psTrace ("psModules.objects", 3, "extreme centroid swing; invalid peak %d, %d\n", source->peak->x, source->peak->y);
-	return (false);
-    }
-    if ((fabs(Mx) > 2.0) || (fabs(My) > 2.0)) {
-	psTrace ("psModules.objects", 3, " big centroid swing; ok peak? %d, %d\n", source->peak->x, source->peak->y);
-    }
-
-    psTrace ("psModules.objects", 5, "id: %d, sky: %f  Mx: %f  My: %f  Sum: %f  X1: %f  Y1: %f  Npix: %d\n", source->id, sky, Mx, My, Sum, X1, Y1, numPixels);
-
-    // add back offset of peak in primary image
-    // also offset from pixel index to pixel coordinate
-    // (the calculation above uses pixel index instead of coordinate)
-    // 0.5 PIX: moments are calculated using the pixel index and converted here to pixel coords
-
-    // we only update the centroid if the position is not supplied from elsewhere
-    bool skipCentroid = false;
-    skipCentroid |= (source->mode  & PM_SOURCE_MODE_EXTERNAL); // skip externally supplied positions
-    skipCentroid |= (source->mode2 & PM_SOURCE_MODE2_MATCHED); // skip sources defined by other image positions
-
-    if (skipCentroid) {
-	source->moments->Mx = source->peak->xf;
-	source->moments->My = source->peak->yf;
-    } else {
-	source->moments->Mx = Mx + xGuess;
-	source->moments->My = My + yGuess;
-    }
-
-    source->moments->Sum = Sum;
-    source->moments->SN  = Sum / sqrt(Var);
-    source->moments->Peak = peakPixel;
-    source->moments->nPixels = numPixels;
-
     return true;
 }
-
-float pmSourceMinKronRadius(psArray *sources, float PSF_SN_LIM) {
-
-    psVector *radii = psVectorAllocEmpty(100, PS_TYPE_F32);
-
-    for (int i = 0; i < sources->n; i++) {
-	pmSource *src = sources->data[i]; // Source of interest
-	if (!src || !src->moments) {
-	    continue;
-	}
-
-	if (src->mode & PM_SOURCE_MODE_BLEND) {
-	    continue;
-	}
-
-	if (!src->moments->nPixels) continue;
-
-	if (src->moments->SN < PSF_SN_LIM) continue;
-
-	// XXX put in Mxx,Myy cut based on clump location
-
-	psVectorAppend(radii, src->moments->Mrf);
-    }
-
-    // find the peak in this image
-    psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
-
-    if (!psVectorStats (stats, radii, NULL, NULL, 0)) {
-	psError(PS_ERR_UNKNOWN, false, "Unable to get image statistics.\n");
-	psFree(stats);
-	return NAN;
-    }
-
-    float minRadius = stats->sampleMedian;
-
-    psFree(radii);
-    psFree(stats);
-    return minRadius;
-}
-
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceOutputs.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceOutputs.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceOutputs.c	(revision 37066)
@@ -26,11 +26,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -107,17 +108,29 @@
 	}
 	if (isfinite(PAR[PM_PAR_SXX]) && isfinite(PAR[PM_PAR_SXY]) && isfinite(PAR[PM_PAR_SYY])) {
-	    axes = pmPSF_ModelToAxes (PAR, model->type);
+	    axes = pmPSF_ModelToAxes (PAR, model->class->useReff);
 	    outputs->psfMajor = axes.major;
 	    outputs->psfMinor = axes.minor;
 	    outputs->psfTheta = axes.theta*PS_DEG_RAD;
+
+	    // some models (PS1_V1, QGAUSS) have an extra 'core' parameter
+	    outputs->psfCore = NAN;
+	    if (model->type == pmModelClassGetType ("PS_MODEL_PS1_V1")) {
+		outputs->psfCore = PAR[PM_PAR_7];
+	    }
+	    if (model->type == pmModelClassGetType ("PS_MODEL_QGAUSS")) {
+		outputs->psfCore = PAR[PM_PAR_7];
+	    }
+
+	    outputs->psfMajorFWHM = model->class->modelSetFWHM(model->params, axes.major);
+	    outputs->psfMinorFWHM = model->class->modelSetFWHM(model->params, axes.minor);
 	} else {
 	    outputs->psfMajor = NAN;
 	    outputs->psfMinor = NAN;
 	    outputs->psfTheta = NAN;
+	    outputs->psfCore = NAN;
 	}
 	outputs->chisq = model->chisq;
 	outputs->nDOF = model->nDOF;
 	outputs->nPix = model->nPix;
-	outputs->apRadius = source->apRadius;
     } else {
 	bool useMoments = pmSourcePositionUseMoments(source);
@@ -137,8 +150,8 @@
 	outputs->psfMinor = NAN;
 	outputs->psfTheta = NAN;
+	outputs->psfCore = NAN;
 	outputs->chisq = NAN;
 	outputs->nDOF = 0;
 	outputs->nPix = 0;
-	outputs->apRadius = NAN;
     }
 
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceOutputs.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceOutputs.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceOutputs.h	(revision 37066)
@@ -26,6 +26,8 @@
     float psfMinor;
     float psfTheta;
+    float psfCore;
+    float psfMajorFWHM;
+    float psfMinorFWHM;
     float chisq;
-    float apRadius;
     int nPix;
     int nDOF;
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourcePhotometry.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourcePhotometry.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourcePhotometry.c	(revision 37066)
@@ -33,11 +33,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -113,4 +114,12 @@
     source->apFluxErr = NAN;
 
+    pmModelStatus badModel = PM_MODEL_STATUS_NONE;
+    badModel |= PM_MODEL_STATUS_BADARGS;
+    badModel |= PM_MODEL_STATUS_OFFIMAGE;
+    badModel |= PM_MODEL_STATUS_NAN_CHISQ;
+    badModel |= PM_MODEL_SERSIC_PCM_FAIL_GUESS;
+    badModel |= PM_MODEL_SERSIC_PCM_FAIL_GRID;
+    badModel |= PM_MODEL_PCM_FAIL_GUESS;
+
     // XXXXXX review:
     // Select the 'best' model -- this is used for PSF_QF,_PERFECT & ???. isPSF is true if this
@@ -162,5 +171,5 @@
         for (int i = 0; i < source->modelFits->n; i++) {
             pmModel *model = source->modelFits->data[i];
-	    if (model->flags & PM_MODEL_STATUS_BADARGS) continue;
+	    if (model->flags & badModel) continue;
             status = pmSourcePhotometryModel (&model->mag, NULL, model);
             if (model == source->modelEXT) foundEXT = true;
@@ -321,5 +330,5 @@
 
     // measure fitMag
-    flux = model->modelFlux (model->params);
+    flux = model->class->modelFlux (model->params);
     if (flux > 0) {
         mag = -2.5*log10(flux);
@@ -350,6 +359,5 @@
 
     bool status;
-    int nPix = 0;
-    status = pmSourcePhotometryAper(&nPix, &source->apMagRaw, &source->apFlux, &source->apFluxErr, model, image, variance, mask, maskVal);
+    status = pmSourcePhotometryAper(&source->apNpixels, &source->apMagRaw, &source->apFlux, &source->apFluxErr, model, image, variance, mask, maskVal);
     if (status) {
 	source->mode |= PM_SOURCE_MODE_AP_MAGS;
@@ -481,5 +489,5 @@
 
             // for the full model, add all points
-            value = fabs(model->modelFunc (NULL, params, coord) - sky);
+            value = fabs(model->class->modelFunc (NULL, params, coord) - sky);
             modelSum += value;
 
@@ -884,5 +892,5 @@
 
             // for the full model, add all points
-            float value = model->modelFunc (NULL, params, coord);
+            float value = model->class->modelFunc (NULL, params, coord);
 
 	    // fprintf (stderr, "%d, %d : %f, %f : %f - %f : %f\n", 
@@ -899,4 +907,40 @@
 
     psFree (coord);
+    return (true);
+}
+
+bool pmSourceChisqModelFlux (pmSource *source, pmModel *model, psImageMaskType maskVal)
+{
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    PS_ASSERT_PTR_NON_NULL(model, false);
+
+    float dC = 0.0;
+    int Npix = 0;
+
+    psVector *params = model->params;
+    psImage  *image = source->pixels;
+    psImage  *modelFlux = source->modelFlux;
+    psImage  *mask = source->maskObj;
+    psImage  *variance = source->variance;
+
+    float Io = params->data.F32[PM_PAR_I0];
+
+    for (int iy = 0; iy < image->numRows; iy++) {
+        for (int ix = 0; ix < image->numCols; ix++) {
+
+	    // skip pixels which are masked
+            if (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] & maskVal) continue;
+
+            if (variance->data.F32[iy][ix] <= 0) continue;
+
+            dC += PS_SQR (image->data.F32[iy][ix] - Io*modelFlux->data.F32[iy][ix]) / variance->data.F32[iy][ix];
+            Npix ++;
+        }
+    }
+    model->nPix = Npix;
+    model->nDOF = Npix - model->nPar;
+    model->chisq = dC;
+    model->chisqNorm = dC / model->nDOF;
+
     return (true);
 }
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourcePlotApResid.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourcePlotApResid.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourcePlotApResid.c	(revision 37066)
@@ -35,11 +35,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourcePlotMoments.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourcePlotMoments.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourcePlotMoments.c	(revision 37066)
@@ -38,11 +38,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourcePlotPSFModel.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourcePlotPSFModel.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourcePlotPSFModel.c	(revision 37066)
@@ -39,11 +39,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -146,5 +147,5 @@
         // force the axis ratio to be < 20.0
         psEllipseAxes axes_mnt = psEllipseMomentsToAxes (moments, 20.0);
-        psEllipseAxes axes_psf = pmPSF_ModelToAxes (PAR, model->type);
+        psEllipseAxes axes_psf = pmPSF_ModelToAxes (PAR, model->class->useReff);
 
         // moments major axis
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceSky.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceSky.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceSky.c	(revision 37066)
@@ -34,11 +34,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceUtils.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceUtils.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceUtils.c	(revision 37066)
@@ -34,11 +34,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 
@@ -64,5 +65,5 @@
     pmModel *model = pmModelAlloc(modelType);
 
-    if (!model->modelGuess(model, source, maskVal, markVal)) {
+    if (!model->class->modelGuess(model, source, maskVal, markVal)) {
 	psFree (model);
 	return NULL;
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceVisual.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceVisual.c	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/objects/pmSourceVisual.c	(revision 37066)
@@ -16,11 +16,12 @@
 #include "pmMoments.h"
 #include "pmModelFuncs.h"
+#include "pmModelClass.h"
 #include "pmModel.h"
 #include "pmModelUtils.h"
-#include "pmModelClass.h"
 #include "pmSourceMasks.h"
 #include "pmSourceExtendedPars.h"
 #include "pmSourceDiffStats.h"
 #include "pmSourceSatstar.h"
+#include "pmSourceLensing.h"
 #include "pmSource.h"
 #include "pmSourceFitModel.h"
@@ -544,7 +545,31 @@
     psFree (model);
 
+    bool dumpData = false;
+
     // pause and wait for user input:
     // continue, save (provide name), ??
-    pmVisualAskUser(&plotPSF);
+retry:
+    pmVisualAskUserOrDump(&plotPSF, &dumpData);
+    if (dumpData) {
+      char name[128];
+      fprintf (stderr, "filename: ");
+      int status = fscanf (stdin, "%127s", name);
+      if (status != 1) {
+	fprintf (stderr, "odd response\n");
+	goto retry;
+      }
+
+      FILE *f = fopen (name, "w");
+      if (!f) {
+	fprintf (stderr, "cannot open %s for output\n", name);
+	goto retry;
+      }
+      for (int i = 0; i < x->n; i++) {
+        float vModel = pmTrend2DEval (trend, x->data.F32[i], y->data.F32[i]);
+	fprintf (f, "%f %f %f %f %d\n", x->data.F32[i], y->data.F32[i], param->data.F32[i], vModel, mask->data.PS_TYPE_VECTOR_MASK_DATA[i]);
+      }
+      fclose (f);
+      goto retry;
+    }
 
     return true;
Index: branches/eam_branches/ipp-ops-20130712/psModules/src/psmodules.h
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/src/psmodules.h	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/src/psmodules.h	(revision 37066)
@@ -95,4 +95,5 @@
 #include <pmAstrometryDistortion.h>
 #include <pmAstrometryVisual.h>
+#include <pmKHcorrect.h>
 
 // the following headers are from psModule:imcombine
@@ -128,5 +129,7 @@
 
 #include <pmModelFuncs.h>
+#include <pmModelClass.h>
 #include <pmModel.h>
+#include <pmModel_CentralPixel.h>
 
 #include <pmSourceMasks.h>
@@ -134,4 +137,5 @@
 #include <pmSourceSatstar.h>
 #include <pmSourceDiffStats.h>
+#include <pmSourceLensing.h>
 #include <pmSource.h>
 #include <pmSourceFitModel.h>
@@ -146,5 +150,4 @@
 #include <pmSourcePlots.h>
 #include <pmPSF_IO.h>
-#include <pmModelClass.h>
 #include <pmModelUtils.h>
 #include <pmSourcePhotometry.h>
Index: branches/eam_branches/ipp-ops-20130712/psModules/test/objects/Makefile.am
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/test/objects/Makefile.am	(revision 37064)
+++ branches/eam_branches/ipp-ops-20130712/psModules/test/objects/Makefile.am	(revision 37066)
@@ -19,4 +19,6 @@
 	tap_pmModelUtils \
 	tap_pmModelClass \
+	tap_pmModel_CentralPixel \
+	tap_pmModel_CentralPixel_v2 \
 	tap_pmPSF \
 	tap_pmTrend2D \
Index: branches/eam_branches/ipp-ops-20130712/psModules/test/objects/tap_pmModel_CentralPixel.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/test/objects/tap_pmModel_CentralPixel.c	(revision 37066)
+++ branches/eam_branches/ipp-ops-20130712/psModules/test/objects/tap_pmModel_CentralPixel.c	(revision 37066)
@@ -0,0 +1,202 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+int main (int argc, char **argv)
+{
+    if (argc != 3) {
+	fprintf (stderr, "USAGE: tap_pmModel_CentralPixel (model) (set)\n");
+	exit (2);
+    }
+
+    int set = atoi(argv[2]);
+
+    psMemId id = psMemGetId();
+
+    plan_tests(240);
+
+    pmModelCP *t0 = pmModelCP_Alloc();
+    ok (t0, "allocated pmModelCP");
+    psFree (t0);
+
+    pmModelCPset *t1 = pmModelCPset_Alloc();
+    ok (t1, "allocated pmModelCPset");
+    psFree (t1);
+    
+    pmModelCPset *cpset = pmModelCP_Load (argv[1]);
+    ok (cpset, "loaded pmModelCPset from file");
+
+    ok (cpset->RmajorNitem ==  4, "correct number of Rmajor values");
+    ok (cpset->AratioNitem ==  7, "correct number of Aratio values");
+    ok (cpset->SindexNitem == 10, "correct number of Sindex values");
+    ok (cpset->images->n == 280, "correct number of CP images");
+
+    pmModelCP *cp = NULL;
+    
+    if (1) {
+	cp = pmModelCP_GetImage (cpset, 0.0, 1.0, 1.0);
+	ok (cp, "returned a cp image");
+	if (cp) {
+	    ok_float_tol (cp->Rmajor, 0.0, 0.001, "got image with correct Rmajor");
+	    ok_float_tol (cp->Aratio, 1.0, 0.001, "got image with correct Aratio");
+	    ok_float_tol (cp->Sindex, 1.0, 0.001, "got image with correct Sindex");
+	}
+
+	cp = pmModelCP_GetImage (cpset, 1.0, 1.0, 1.0);
+	ok (cp, "returned a cp image");
+	if (cp) {
+	    ok_float_tol (cp->Rmajor, 1.0, 0.001, "got image with correct Rmajor");
+	    ok_float_tol (cp->Aratio, 1.0, 0.001, "got image with correct Aratio");
+	    ok_float_tol (cp->Sindex, 1.0, 0.001, "got image with correct Sindex");
+	}
+    
+	cp = pmModelCP_GetImage (cpset, 0.0, 0.4, 1.0);
+	ok (cp, "returned a cp image");
+	if (cp) {
+	    ok_float_tol (cp->Rmajor, 0.0, 0.001, "got image with correct Rmajor");
+	    ok_float_tol (cp->Aratio, 0.4, 0.001, "got image with correct Aratio");
+	    ok_float_tol (cp->Sindex, 1.0, 0.001, "got image with correct Sindex");
+	}
+    
+	cp = pmModelCP_GetImage (cpset, 0.0, 0.4, 3.5);
+	ok (cp, "returned a cp image");
+	if (cp) {
+	    ok_float_tol (cp->Rmajor, 0.0, 0.001, "got image with correct Rmajor");
+	    ok_float_tol (cp->Aratio, 0.4, 0.001, "got image with correct Aratio");
+	    ok_float_tol (cp->Sindex, 3.5, 0.001, "got image with correct Sindex");
+	}
+    }
+    
+    float valuePixel = NAN;
+    float valueModel = NAN;
+
+    switch (set) {
+      case 0:
+	cp = pmModelCP_GetImage (cpset, 1.0, 1.0, 1.0);
+	valuePixel = pmModelCP_GetFlux (cp, 0.0, 0.0, 0.0);
+	valueModel = pmModelCP_FullSersic (0.0, 0.0, 0.0, pow(10.0, cp->Rmajor), cp->Aratio, cp->Sindex);
+	fprintf (stdout, "%f / %f = %f\n", valuePixel, valueModel, valuePixel / valueModel);
+	break;
+
+      case 1:
+	for (int i = 0; i < cpset->images->n; i++) {
+	    cp = cpset->images->data[i];
+	    valuePixel = pmModelCP_GetFlux (cp, 0.0, 0.0, 0.0);
+	    valueModel = pmModelCP_FullSersic (0.0, 0.0, 0.0, pow(10.0, cp->Rmajor), cp->Aratio, cp->Sindex);
+	    fprintf (stdout, "%f / %f = %f | %f  %f  %f\n", valuePixel, valueModel, valuePixel / valueModel, pow(10.0, cp->Rmajor), cp->Aratio, cp->Sindex);
+	}
+	break;
+
+      case 2:
+	for (int i = 0; i < cpset->images->n; i++) {
+	    cp = cpset->images->data[i];
+	    valuePixel = pmModelCP_GetFlux (cp, 0.0, 0.0, 30.0);
+	    valueModel = pmModelCP_FullSersic (0.0, 0.0, 30.0, pow(10.0, cp->Rmajor), cp->Aratio, cp->Sindex);
+	    fprintf (stdout, "%f / %f = %f | %f  %f  %f\n", valuePixel, valueModel, valuePixel / valueModel, pow(10.0, cp->Rmajor), cp->Aratio, cp->Sindex);
+	}
+	break;
+
+      case 3:
+	for (int i = 0; i < cpset->images->n; i++) {
+	    cp = cpset->images->data[i];
+	    valuePixel = pmModelCP_GetFlux (cp, 0.0, 0.5, 0.0);
+	    valueModel = pmModelCP_FullSersic (0.0, 0.5, 0.0, pow(10.0, cp->Rmajor), cp->Aratio, cp->Sindex);
+	    fprintf (stdout, "%f / %f = %f | %f  %f  %f\n", valuePixel, valueModel, valuePixel / valueModel, pow(10.0, cp->Rmajor), cp->Aratio, cp->Sindex);
+	}
+	break;
+
+      case 4:
+	for (int i = 0; i < cpset->images->n; i++) {
+	    cp = cpset->images->data[i];
+	    valuePixel = pmModelCP_GetFlux (cp, 0.5, 0.5, 0.0);
+	    valueModel = pmModelCP_FullSersic (0.5, 0.5, 0.0, pow(10.0, cp->Rmajor), cp->Aratio, cp->Sindex);
+	    fprintf (stdout, "%f / %f = %f | %f  %f  %f\n", valuePixel, valueModel, valuePixel / valueModel, pow(10.0, cp->Rmajor), cp->Aratio, cp->Sindex);
+	}
+	break;
+
+      case 5:
+	for (int i = 0; i < cpset->images->n; i++) {
+	    cp = cpset->images->data[i];
+	    valuePixel = pmModelCP_GetFlux (cp, -0.5, 0.5, 30.0);
+	    valueModel = pmModelCP_FullSersic (-0.5, 0.5, 30.0, pow(10.0, cp->Rmajor), cp->Aratio, cp->Sindex);
+	    fprintf (stdout, "%f / %f = %f | %f  %f  %f\n", valuePixel, valueModel, valuePixel / valueModel, pow(10.0, cp->Rmajor), cp->Aratio, cp->Sindex);
+	}
+	break;
+
+      case 6:
+	for (int i = 0; i < cpset->images->n; i++) {
+	    cp = cpset->images->data[i];
+	    valuePixel = pmModelCP_GetFlux (cp, 0.0, -0.5, 0.0);
+	    valueModel = pmModelCP_FullSersic (0.0, -0.5, 0.0, pow(10.0, cp->Rmajor), cp->Aratio, cp->Sindex);
+	    fprintf (stdout, "%f / %f = %f | %f  %f  %f\n", valuePixel, valueModel, valuePixel / valueModel, pow(10.0, cp->Rmajor), cp->Aratio, cp->Sindex);
+	}
+	break;
+
+      case 7:
+	cp = cpset->images->data[224];
+	float delta = 1.0 / 11.0;
+	float offset = -10*delta;
+	for (float dx = offset; dx <= 1.0; dx += delta) {
+	    valuePixel = pmModelCP_GetFlux (cp, dx, 0.0, 0.0);
+	    valueModel = pmModelCP_FullSersic (dx, 0.0, 0.0, pow(10.0, cp->Rmajor), cp->Aratio, cp->Sindex);
+	    fprintf (stdout, "%f / %f = %f | %f  %f  %f\n", valuePixel, valueModel, valuePixel / valueModel, pow(10.0, cp->Rmajor), cp->Aratio, cp->Sindex);
+	}
+	break;
+
+      case 8:
+	cp = cpset->images->data[224];
+	valuePixel = pmModelCP_GetFlux (cp, 0.0, 0.0, 45.0);
+	valueModel = pmModelCP_FullSersic (0.0, 0.0, 45.0, pow(10.0, cp->Rmajor), cp->Aratio, cp->Sindex);
+	fprintf (stdout, "%f / %f = %f | %f  %f  %f\n", valuePixel, valueModel, valuePixel / valueModel, pow(10.0, cp->Rmajor), cp->Aratio, cp->Sindex);
+	break;
+
+      case 9:
+	for (int i = 0; i < cpset->images->n; i++) {
+	    cp = cpset->images->data[i];
+	    valuePixel = pmModelCP_GetFlux (cp, -0.5, -0.5, 0.0);
+	    valueModel = pmModelCP_FullSersic (-0.5, -0.5, 0.0, pow(10.0, cp->Rmajor), cp->Aratio, cp->Sindex);
+	    fprintf (stdout, "%f / %f = %f | %f  %f  %f\n", valuePixel, valueModel, valuePixel / valueModel, pow(10.0, cp->Rmajor), cp->Aratio, cp->Sindex);
+	}
+	break;
+      case 10:
+	for (int i = 0; i < cpset->images->n; i++) {
+	    cp = cpset->images->data[i];
+	    valuePixel = pmModelCP_GetFlux (cp, 0.5, 0.0, 0.0);
+	    valueModel = pmModelCP_FullSersic (0.5, 0.0, 0.0, pow(10.0, cp->Rmajor), cp->Aratio, cp->Sindex);
+	    fprintf (stdout, "%f / %f = %f | %f  %f  %f\n", valuePixel, valueModel, valuePixel / valueModel, pow(10.0, cp->Rmajor), cp->Aratio, cp->Sindex);
+	}
+	break;
+
+      case 11:
+	for (int i = 0; i < cpset->images->n; i++) {
+	    cp = cpset->images->data[i];
+	    valuePixel = pmModelCP_GetFlux (cp, -0.5, 0.0, 0.0);
+	    valueModel = pmModelCP_FullSersic (-0.5, 0.0, 0.0, pow(10.0, cp->Rmajor), cp->Aratio, cp->Sindex);
+	    fprintf (stdout, "%f / %f = %f | %f  %f  %f\n", valuePixel, valueModel, valuePixel / valueModel, pow(10.0, cp->Rmajor), cp->Aratio, cp->Sindex);
+	}
+	break;
+      case 12:
+	psTimerStart ("test");
+	for (int i = 0; i < cpset->images->n; i++) {
+	    cp = cpset->images->data[i];
+	    valuePixel = pmModelCP_GetFlux (cp, 0.0, 0.0, 0.0);
+	}
+	fprintf (stderr, "CP code: %f\n", psTimerMark ("test"));
+	psTimerStart ("test");
+	for (int i = 0; i < cpset->images->n; i++) {
+	    cp = cpset->images->data[i];
+	    valueModel = pmModelCP_FullSersic (0.0, 0.0, 0.0, pow(10.0, cp->Rmajor), cp->Aratio, cp->Sindex);
+	}
+	fprintf (stderr, "Full code: %f\n", psTimerMark ("test"));
+	break;
+    }
+
+    psFree (cpset);
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+
+    return exit_status();
+}
Index: branches/eam_branches/ipp-ops-20130712/psModules/test/objects/tap_pmModel_CentralPixel_v2.c
===================================================================
--- branches/eam_branches/ipp-ops-20130712/psModules/test/objects/tap_pmModel_CentralPixel_v2.c	(revision 37066)
+++ branches/eam_branches/ipp-ops-20130712/psModules/test/objects/tap_pmModel_CentralPixel_v2.c	(revision 37066)
@@ -0,0 +1,52 @@
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+int main (int argc, char **argv)
+{
+    if (argc != 8) {
+	fprintf (stderr, "USAGE: tap_pmModel_CentralPixel (model) (dx) (dy) (theta) (Reff) (Arat) (Sidx)\n");
+	exit (2);
+    }
+
+    float dx = atof(argv[2]);
+    float dy = atof(argv[3]);
+    float theta = atof(argv[4]);
+
+    float Reff = atof(argv[5]);
+    float Arat = atof(argv[6]);
+    float Sidx = atof(argv[7]);
+
+    psMemId id = psMemGetId();
+
+    plan_tests(6);
+
+    pmModelCPset *cpset = pmModelCP_Load (argv[1]);
+    ok (cpset, "loaded pmModelCPset from file");
+
+    ok (cpset->RmajorNitem ==  4, "correct number of Rmajor values");
+    ok (cpset->AratioNitem ==  7, "correct number of Aratio values");
+    ok (cpset->SindexNitem == 10, "correct number of Sindex values");
+    ok (cpset->images->n == 280, "correct number of CP images");
+
+    pmModelCP *cp = NULL;
+    
+    float valuePixel = NAN;
+    float valueModel = NAN;
+
+    cp = pmModelCP_GetImage (cpset, log10(Reff), Arat, Sidx);
+    valuePixel = pmModelCP_GetFlux (cp, dx, dy, theta);
+    valueModel = pmModelCP_FullSersic (dx, dy, theta, Reff, Arat, Sidx);
+    fprintf (stdout, "%f / %f = %f\n", valuePixel, valueModel, valuePixel / valueModel);
+
+    fprintf (stdout, "%f, %f, %f\n", cp->Rmajor, cp->Aratio, cp->Sindex);
+
+    psFree (cpset);
+    ok(!psMemCheckLeaks (id, NULL, stderr, false), "no memory leaks");
+
+    return exit_status();
+}
