Index: trunk/ppStack/src/ppStack.h
===================================================================
--- trunk/ppStack/src/ppStack.h	(revision 21052)
+++ trunk/ppStack/src/ppStack.h	(revision 21092)
@@ -154,8 +154,8 @@
 /// Calculate transparency differences between images
 ///
-/// Corrects the source PSF photometry to a common system
-bool ppStackSourcesTransparency(const psArray *sourceLists, // Sources for each input
-                                const pmFPAview *view, // View to readout
-                                const pmConfig *config // Configuration
+/// Corrects the source PSF photometry to a common system.  Return the sum of the exposure times.
+float ppStackSourcesTransparency(const psArray *sourceLists, // Sources for each input
+                                 const pmFPAview *view, // View to readout
+                                 const pmConfig *config // Configuration
     );
 
Index: trunk/ppStack/src/ppStackLoop.c
===================================================================
--- trunk/ppStack/src/ppStackLoop.c	(revision 21052)
+++ trunk/ppStack/src/ppStackLoop.c	(revision 21092)
@@ -254,4 +254,5 @@
     psArray *sourceLists = psArrayAlloc(num); // Individual lists of sources for matching
     pmPSF *targetPSF = NULL;            // Target PSF
+    float sumExposure = NAN;            // Sum of exposure times
     if (psMetadataLookupBool(NULL, config->arguments, "HAVE.PSF")) {
         pmFPAfileActivate(config->files, false, NULL);
@@ -348,5 +349,6 @@
 
         // Zero point calibration
-        if (!ppStackSourcesTransparency(sourceLists, view, config)) {
+        sumExposure = ppStackSourcesTransparency(sourceLists, view, config);
+        if (!isfinite(sumExposure) || sumExposure <= 0) {
             psError(PS_ERR_UNKNOWN, false, "Unable to calculate transparency differences");
             psFree(sourceLists);
@@ -407,4 +409,6 @@
     psVector *weightings = psVectorAlloc(num, PS_TYPE_F32); // Combination weightings for images (1/noise^2)
     psVectorInit(weightings, NAN);
+    psList *fpaList = psListAlloc(NULL); // List of input FPAs, for concept averaging
+    psList *cellList = psListAlloc(NULL); // List of input cells, for concept averaging
     for (int i = 0; i < num; i++) {
         psTrace("ppStack", 2, "Convolving input %d of %d to target PSF....\n", i, num);
@@ -475,5 +479,10 @@
         writeImage(weightNames->data[i], hdu->header, readout->weight, config);
 
-        cells->data[i] = psMemIncrRefCounter(readout->parent);
+        pmCell *inCell = readout->parent; // Input cell
+
+        psListAdd(cellList, PS_LIST_TAIL, inCell);
+        psListAdd(fpaList, PS_LIST_TAIL, inCell->parent->parent);
+
+        cells->data[i] = psMemIncrRefCounter(inCell);
         if (!filesIterateUp(config)) {
             return false;
@@ -496,4 +505,30 @@
         return false;
     }
+
+
+    // Update concepts for output
+    {
+        pmFPAview view;                 // View for output
+        view.chip = view.cell = view.readout = 0;
+        pmCell *outCell = pmFPAfileThisCell(config->files, &view, "PPSTACK.OUTPUT"); // Output cell
+        pmFPA *outFPA = outCell->parent->parent; // Output FPA
+        pmConceptsAverageFPAs(outFPA, fpaList);
+        pmConceptsAverageCells(outCell, cellList, NULL, NULL, true);
+        psFree(fpaList);
+        psFree(cellList);
+
+        // Update the value of a concept
+#define UPDATE_CONCEPT(SOURCE, NAME, VALUE) { \
+            psMetadataItem *item = psMetadataLookup(SOURCE->concepts, NAME); \
+            psAssert(item, "Concept should be present"); \
+            psAssert(item->type == PS_TYPE_F32, "Concept should be F32"); \
+            item->data.F32 = VALUE; \
+        }
+
+        UPDATE_CONCEPT(outFPA,  "FPA.EXPOSURE",  sumExposure);
+        UPDATE_CONCEPT(outCell, "CELL.EXPOSURE", sumExposure);
+        UPDATE_CONCEPT(outCell, "CELL.DARKTIME", NAN);
+    }
+
 
     // Reject images out-of-hand on the basis of their match chi^2
@@ -626,25 +661,4 @@
             return false;
         }
-
-        psList *fpaList = psListAlloc(NULL); // List of input FPAs, for concept averaging
-        psList *cellList = psListAlloc(NULL); // List of input cells, for concept averaging
-        for (int i = 0; i < num; i++) {
-            pmCell *inCell = cells->data[i]; // Input cell
-            if (!inCell) {
-                exptimes->data.F32[i] = 0.0;
-                continue;
-            }
-            exptimes->data.F32[i] = psMetadataLookupF32(NULL, inCell->concepts, "CELL.EXPOSURE");
-            psListAdd(cellList, PS_LIST_TAIL, inCell);
-            psListAdd(fpaList, PS_LIST_TAIL, inCell->parent->parent);
-        }
-
-        // Copy concepts
-        pmChip *outChip = outCell->parent;  // Output chip
-        pmFPA *outFPA = outChip->parent;    // Output FPA
-        pmConceptsAverageFPAs(outFPA, fpaList);
-        pmConceptsAverageCells(outCell, cellList, NULL, NULL, true);
-        psFree(fpaList);
-        psFree(cellList);
 
         psFree(cells);
@@ -977,10 +991,8 @@
     // Close up
     bool wcsDone = false;           // Have we done the WCS?
-    float totExposure = 0.0;            // Total exposure time
     for (int i = 0; i < num; i++) {
         if (inputMask->data.U8[i]) {
             continue;
         }
-        totExposure += exptimes->data.F32[i];
 
         ppStackThread *thread = stack->threads->data[0]; // Representative stack
Index: trunk/ppStack/src/ppStackSources.c
===================================================================
--- trunk/ppStack/src/ppStackSources.c	(revision 21052)
+++ trunk/ppStack/src/ppStackSources.c	(revision 21092)
@@ -10,9 +10,9 @@
 
 
-bool ppStackSourcesTransparency(const psArray *sourceLists, const pmFPAview *view, const pmConfig *config)
+float ppStackSourcesTransparency(const psArray *sourceLists, const pmFPAview *view, const pmConfig *config)
 {
-    PS_ASSERT_ARRAY_NON_NULL(sourceLists, false);
-    PS_ASSERT_PTR_NON_NULL(view, false);
-    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_ARRAY_NON_NULL(sourceLists, NAN);
+    PS_ASSERT_PTR_NON_NULL(view, NAN);
+    PS_ASSERT_PTR_NON_NULL(config, NAN);
 
 #ifdef TESTING
@@ -46,5 +46,5 @@
     if (!airmassZP) {
         psError(PS_ERR_UNKNOWN, false, "Unable to find ZP.AIRMASS in recipe.");
-        return false;
+        return NAN;
     }
 
@@ -69,5 +69,5 @@
                     exptime, airmass, expFilter);
             psFree(zp);
-            return false;
+            return NAN;
         }
 
@@ -79,10 +79,10 @@
                         "Unable to find airmass term (ZP.AIRMASS) for filter %s", filter);
                 psFree(zp);
-                return false;
+                return NAN;
             }
         } else if (strcmp(filter, expFilter) != 0) {
             psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Filters don't match: %s vs %s", filter, expFilter);
             psFree(zp);
-            return false;
+            return NAN;
         }
 
@@ -95,5 +95,5 @@
         psError(PS_ERR_UNKNOWN, false, "Unable to match sources");
         psFree(zp);
-        return false;
+        return NAN;
     }
     psVector *trans = pmSourceMatchRelphot(matches, zp, iter, tol, starLimit, transIter, transRej,
@@ -128,5 +128,5 @@
     if (!trans) {
         psError(PS_ERR_UNKNOWN, false, "Unable to measure transparencies");
-        return false;
+        return NAN;
     }
 
@@ -158,5 +158,5 @@
             psError(PS_ERR_UNKNOWN, false, "Unable to match sources");
             psFree(zp);
-            return false;
+            return NAN;
         }
         psVector *trans = pmSourceMatchRelphot(matches, zp, iter, tol, starLimit, transIter, transRej,
@@ -165,4 +165,5 @@
     }
 #endif
-    return true;
+
+    return sumExpTime;
 }
