IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Changeset 23341


Ignore:
Timestamp:
Mar 17, 2009, 10:00:02 AM (17 years ago)
Author:
Paul Price
Message:

Reorganising ppStackLoop: splitting into multiple separate functions which are joined using a common data carrier, ppStackOptions (which aren't really 'options', but that's the word I chose...).

Location:
trunk/ppStack/src
Files:
13 added
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/ppStack/src/Makefile.am

    r23229 r23341  
    1717        ppStackArguments.c      \
    1818        ppStackCamera.c         \
     19        ppStackFiles.c          \
    1920        ppStackLoop.c           \
    2021        ppStackPSF.c            \
    2122        ppStackReadout.c        \
    22         ppStackPhotometry.c     \
    2323        ppStackVersion.c        \
    2424        ppStackMatch.c          \
    2525        ppStackSources.c        \
    26         ppStackThread.c
     26        ppStackThread.c         \
     27        ppStackOptions.c        \
     28        ppStackSetup.c          \
     29        ppStackPrepare.c        \
     30        ppStackConvolve.c       \
     31        ppStackCombineInitial.c \
     32        ppStackReject.c         \
     33        ppStackCombineFinal.c   \
     34        ppStackCleanup.c        \
     35        ppStackPhotometry.c     \
     36        ppStackFinish.c
    2737
    2838noinst_HEADERS =                \
    29         ppStack.h
     39        ppStack.h               \
     40        ppStackLoop.h           \
     41        ppStackOptions.h        \
     42        ppStackThread.h
    3043
    3144CLEANFILES = *~
  • trunk/ppStack/src/ppStack.c

    r23242 r23341  
    99
    1010#include "ppStack.h"
     11#include "ppStackLoop.h"
    1112
    1213#define TIMER_NAME "PPSTACK"            // Name of timer
  • trunk/ppStack/src/ppStack.h

    r23195 r23341  
    1 #ifndef PP_STACK_H
    2 #define PP_STACK_H
     1#ifndef PPSTACK_H
     2#define PPSTACK_H
    33
    44#define PPSTACK_RECIPE "PPSTACK"        // Name of the recipe
     
    1818} ppStackMask;
    1919
    20 // Thread for stacking chunks
    21 //
    22 // Each input file contributes a readout, into which is read a chunk from that file
    23 typedef struct {
    24     psArray *readouts;                  // Input readouts to read and stack
    25     bool read;                          // Has the scan been read?
    26     bool busy;                          // Is the scan being processed?
    27     int firstScan;                      // First row of the chunk to be read for this group
    28     int lastScan;                       // Last row of the chunk to be read for this group
    29 } ppStackThread;
     20// List of files
     21typedef enum {
     22    PPSTACK_FILES_PREPARE,              // Files for preparation
     23    PPSTACK_FILES_CONVOLVE,             // Files for convolution
     24    PPSTACK_FILES_COMBINE,              // Files for combination
     25    PPSTACK_FILES_PHOT                  // Files for photometry
     26} ppStackFileList;
    3027
    31 // Allocator
    32 ppStackThread *ppStackThreadAlloc(psArray *readouts // Inputs readouts to read and stack
    33     );
    34 
    35 // Data for threads
    36 typedef struct {
    37     psArray *threads;                   // Threads doing stacking
    38     int lastScan;                       // Last row that's been read
    39     psArray *imageFits;                 // FITS file pointers for images
    40     psArray *maskFits;                  // FITS file pointers for masks
    41     psArray *varianceFits;                // FITS file pointers for variances
    42 } ppStackThreadData;
    43 
    44 // Set up thread data
    45 ppStackThreadData *ppStackThreadDataSetup(const psArray *cells, // Array of input cells
    46                                           const psArray *imageNames, // Names of images to read
    47                                           const psArray *maskNames, // Names of masks to read
    48                                           const psArray *varianceNames, // Names of variance maps to read
    49                                           const psArray *covariances, // Covariance matrices (already read)
    50                                           const pmConfig *config // Configuration
    51     );
    52 
    53 // Read chunk into the first available file thread
    54 ppStackThread *ppStackThreadRead(bool *status, // Status of read
    55                                  ppStackThreadData *stack, // Stacks available for reading
    56                                  pmConfig *config, // Configuration
    57                                  int numChunk, // Chunk number (only for interest)
    58                                  int overlap // Overlap between subsequent scans
    59     );
    60 
    61 // Initialise the threads
    62 void ppStackThreadInit(void);
    6328
    6429// Setup command-line arguments
     
    7338// Parse cameras
    7439bool ppStackCamera(pmConfig *config     // Configuration
    75     );
    76 
    77 // Loop over the inputs, doing the combination
    78 bool ppStackLoop(pmConfig *config       // Configuration
    7940    );
    8041
     
    12788    );
    12889
    129 // Perform photometry on stack
    130 bool ppStackPhotometry(pmConfig *config, // Configuration
    131                        const pmReadout *readout, // Readout to be photometered
    132                        const pmFPAview *view // View to readout
    133     );
    134 
    13590// Return software version
    13691psString ppStackVersion(void);
     
    171126    );
    172127
     128/// Dump memory debugging information
     129void ppStackMemDump(
     130    const char *name                    ///< Stage name, for inclusion in the output file name
     131    );
     132
     133/// Activate/deactivate a list of files
     134void ppStackFileActivation(
     135    pmConfig *config,                   // Configuration
     136    ppStackFileList list,               // Files to turn on/off
     137    bool state                          // Activation state
     138    );
     139
     140// Activate/deactivate a single element for a list
     141void ppStackFileActivationSingle(
     142    pmConfig *config,                   // Configuration
     143    ppStackFileList list,               // Files to turn on/off
     144    bool state,                         // Activation state
     145    int num                             // Number of file in sequence
     146    );
     147
     148/// Iterate down the hierarchy, loading files
     149///
     150/// We can get away with this simplistic treatment of the FPA hierarchy because we're working on skycells.
     151pmFPAview *ppStackFilesIterateDown(
     152    pmConfig *config                    // Configuration
     153    );
     154
     155/// Iterate up the hierarchy, writing files
     156///
     157/// We can get away with this simplistic treatment of the FPA hierarchy because we're working on skycells.
     158bool ppStackFilesIterateUp(
     159    pmConfig *config                    // Configuration
     160    );
     161
     162/// Write an image to a FITS file
     163bool ppStackWriteImage(
     164    const char *name,                   // Name of image
     165    psMetadata *header,                 // Header
     166    const psImage *image,               // Image
     167    pmConfig *config                    // Configuration
     168    );
     169
     170
    173171#endif
  • trunk/ppStack/src/ppStackLoop.c

    r23259 r23341  
    44
    55#include <stdio.h>
    6 #include <unistd.h>
    7 #include <string.h>
    8 #include <libgen.h>
    96#include <pslib.h>
    107#include <psmodules.h>
    11 #include <ppStats.h>
    128
    139#include "ppStack.h"
    14 
    15 //#define TESTING
    16 
    17 #define WCS_TOLERANCE 0.001             // Tolerance for WCS
    18 #define PIXELS_BUFFER 1024              // Initial size of pixel lists
    19 
    20 // Here follows lists of files for activation/deactivation at various stages.  Each must be NULL-terminated.
    21 
    22 // Files required in preparation for convolution
    23 static char *prepareFiles[] = { "PPSTACK.INPUT.PSF", "PPSTACK.INPUT.SOURCES", "PPSTACK.TARGET.PSF", NULL };
    24 
    25 // Files required for the convolution
    26 static char *convolveFiles[] = { "PPSTACK.INPUT", "PPSTACK.INPUT.MASK", "PPSTACK.INPUT.VARIANCE", NULL };
    27 
    28 // Output files for the combination
    29 static char *combineFiles[] = { "PPSTACK.OUTPUT", "PPSTACK.OUTPUT.MASK", "PPSTACK.OUTPUT.VARIANCE",
    30                                 "PPSTACK.OUTPUT.JPEG1", "PPSTACK.OUTPUT.JPEG2", NULL };
    31 
    32 // Files for photometry
    33 static char *photFiles[] = { "PSPHOT.INPUT", "PSPHOT.OUTPUT", "PSPHOT.RESID", "PSPHOT.BACKMDL",
    34                              "PSPHOT.BACKMDL.STDEV", "PSPHOT.BACKGND", "PSPHOT.BACKSUB",
    35                              "SOURCE.PLOT.MOMENTS", "SOURCE.PLOT.PSFMODEL", "SOURCE.PLOT.APRESID",
    36                              "PSPHOT.INPUT.CMF", NULL };
    37 
    38 static void memDump(const char *name)
    39 {
    40     return;
    41 
    42     static int num = 0;                 // Counter, to make files unique and give an idea of sequence
    43 
    44     psString filename = NULL;           // Name of file
    45     psStringAppend(&filename, "memdump_%s_%03d.txt", name, num);
    46     FILE *memFile = fopen(filename, "w");
    47     psFree(filename);
    48 
    49     psMemBlock **leaks = NULL;
    50     int numLeaks = psMemCheckLeaks(0, &leaks, NULL, true);
    51     fprintf(memFile, "# MemBlock Size Source\n");
    52     unsigned long total = 0;            // Total memory used
    53     for (int i = 0; i < numLeaks; i++) {
    54         psMemBlock *mb = leaks[i];
    55         fprintf(memFile, "%12lu\t%12zd\t%s:%d\n", mb->id, mb->userMemorySize,
    56                 mb->file, mb->lineno);
    57         total += mb->userMemorySize;
    58     }
    59     fclose(memFile);
    60     psFree(leaks);
    61 
    62     fprintf(stderr, "Memdump %s %d: Memory use: %ld, sbrk: %p\n", name, num, total, sbrk(0));
    63     num++;
    64 }
    65 
    66 
    67 
    68 // Activate/deactivate a list of files
    69 static void fileActivation(pmConfig *config, // Configuration
    70                            char **files, // Files to turn on/off
    71                            bool state   // Activation state
    72     )
    73 {
    74     assert(config);
    75     assert(files);
    76 
    77     for (int i = 0; files[i] != NULL; i++) {
    78         pmFPAfileActivate(config->files, state, files[i]);
    79     }
    80     return;
    81 }
    82 
    83 // Activate/deactivate a single element for a list
    84 static void fileActivationSingle(pmConfig *config, // Configuration
    85                                  char **files, // Files to turn on/off
    86                                  bool state,   // Activation state
    87                                  int num // Number of file in sequence
    88                                  )
    89 {
    90     assert(config);
    91     assert(files);
    92 
    93     for (int i = 0; files[i] != NULL; i++) {
    94         pmFPAfileActivateSingle(config->files, state, files[i], num); // Activated file
    95     }
    96     return;
    97 }
    98 
    99 // Iterate down the hierarchy, loading files; we can get away with this because we're working on skycells
    100 static pmFPAview *filesIterateDown(pmConfig *config // Configuration
    101                                   )
    102 {
    103     assert(config);
    104 
    105     pmFPAview *view = pmFPAviewAlloc(0);// Pointer into FPA hierarchy
    106     if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
    107         return NULL;
    108     }
    109     view->chip = 0;
    110     if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
    111         return NULL;
    112     }
    113     view->cell = 0;
    114     if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
    115         return NULL;
    116     }
    117     view->readout = 0;
    118     if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
    119         return NULL;
    120     }
    121     return view;
    122 }
    123 
    124 // Iterate up the hierarchy, writing files; we can get away with this because we're working on skycells
    125 static bool filesIterateUp(pmConfig *config // Configuration
    126                            )
    127 {
    128     assert(config);
    129 
    130     pmFPAview *view = pmFPAviewAlloc(0);// Pointer into FPA hierarchy
    131     view->chip = view->cell = view->readout = 0;
    132     if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
    133         return false;
    134     }
    135     view->readout = -1;
    136     if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
    137         return false;
    138     }
    139     view->cell = -1;
    140     if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
    141         return false;
    142     }
    143     view->chip = -1;
    144     if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
    145         return false;
    146     }
    147     psFree(view);
    148     return true;
    149 }
    150 
    151 // Write an image to a FITS file
    152 static bool writeImage(const char *name, // Name of image
    153                        psMetadata *header, // Header
    154                        const psImage *image, // Image
    155                        pmConfig *config // Configuration
    156                        )
    157 {
    158     assert(name);
    159     assert(image);
    160 
    161     psString resolved = pmConfigConvertFilename(name, config, true, true); // Resolved file name
    162     psFits *fits = psFitsOpen(resolved, "w");
    163     if (!fits) {
    164         psError(PS_ERR_IO, false, "Unable to open FITS file %s to write image.", resolved);
    165         psFree(resolved);
    166         return false;
    167     }
    168     if (!psFitsWriteImage(fits, header, image, 0, NULL)) {
    169         psError(PS_ERR_IO, false, "Unable to write FITS image %s.", resolved);
    170         psFitsClose(fits);
    171         psFree(resolved);
    172         return false;
    173     }
    174     psFitsClose(fits);
    175     psFree(resolved);
    176     return true;
    177 }
    178 
     10#include "ppStackLoop.h"
    17911
    18012bool ppStackLoop(pmConfig *config)
     
    18315
    18416    psTimerStart("PPSTACK_TOTAL");
     17    psTimerStart("PPSTACK_STEPS");
    18518
    186     psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
    187     psAssert(recipe, "We've thrown an error on this before.");
     19    ppStackOptions *options = ppStackOptionsAlloc(); // Options for stacking
    18820
    189     bool mdok;                          // Status of MD lookup
    190     bool tempDelete = psMetadataLookupBool(&mdok, recipe, "TEMP.DELETE"); // Delete temporary files?
    191     const char *tempDir = psMetadataLookupStr(NULL, recipe, "TEMP.DIR"); // Directory for temporary images
    192     if (!tempDir) {
    193         psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to find TEMP.DIR in recipe");
     21    // Setup
     22    psTrace("ppStack", 1, "Setup....\n");
     23    if (!ppStackSetup(options, config)) {
     24        psError(PS_ERR_UNKNOWN, false, "Unable to setup.");
     25        psFree(options);
     26        return false;
     27    }
     28    psLogMsg("ppStack", PS_LOG_INFO, "Stage 0: Setup: %f sec", psTimerClear("PPSTACK_STEPS"));
     29    ppStackMemDump("setup");
     30
     31
     32    // Preparation for stacking
     33    psTrace("ppStack", 1, "Preparation for stacking: merging sources, determining target PSF....\n");
     34    if (!ppStackPrepare(options, config)) {
     35        psError(PS_ERR_UNKNOWN, false, "Unable to prepare for stacking.");
     36        psFree(options);
     37        return false;
     38    }
     39    psLogMsg("ppStack", PS_LOG_INFO, "Stage 1: Load Sources and Generate Target PSF: %f sec",
     40             psTimerClear("PPSTACK_STEPS"));
     41    ppStackMemDump("prepare");
     42
     43
     44    // Convolve inputs
     45    psTrace("ppStack", 1, "Convolving inputs to target PSF....\n");
     46    if (!ppStackConvolve(options, config)) {
     47        psError(PS_ERR_UNKNOWN, false, "Unable to convolve images.");
     48        psFree(options);
     49        return false;
     50    }
     51    psLogMsg("ppStack", PS_LOG_INFO, "Stage 2: Generate Convolutions and Save: %f sec",
     52             psTimerClear("PPSTACK_STEPS"));
     53    ppStackMemDump("convolve");
     54
     55
     56    // Start threading
     57    ppStackThreadInit();
     58    ppStackThreadData *stack = ppStackThreadDataSetup(options, config);
     59    if (!stack) {
     60        psError(PS_ERR_IO, false, "Unable to initialise stack threads.");
     61        psFree(options);
    19462        return false;
    19563    }
    19664
    197     psString outputName = psStringCopy(psMetadataLookupStr(NULL, config->arguments, "OUTPUT")); // Name for temporary files
    198     const char *tempName = basename(outputName);
    199     if (!tempName) {
    200         psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to construct basename for temporary files.");
     65    // Initial combination
     66    psTrace("ppStack", 1, "Initial stack of convolved images....\n");
     67    if (!ppStackCombineInitial(stack, options, config)) {
     68        psError(PS_ERR_UNKNOWN, false, "Unable to perform initial combination.");
     69        psFree(stack);
     70        psFree(options);
    20171        return false;
    20272    }
     73    psLogMsg("ppStack", PS_LOG_INFO, "Stage 2: Generate Convolutions and Save: %f sec",
     74             psTimerClear("PPSTACK_STEPS"));
     75    ppStackMemDump("convolve");
     76    psLogMsg("ppStack", PS_LOG_INFO, "Stage 4: Make Initial Stack: %f sec", psTimerClear("PPSTACK_STEPS"));
    20377
    204     const char *tempImage = psMetadataLookupStr(NULL, recipe, "TEMP.IMAGE"); // Suffix for temporary images
    205     const char *tempMask = psMetadataLookupStr(NULL, recipe, "TEMP.MASK"); // Suffix for temporary masks
    206     const char *tempVariance = psMetadataLookupStr(NULL, recipe, "TEMP.VARIANCE"); // Suffix for temp variance maps
    207     if (!tempImage || !tempMask || !tempVariance) {
    208         psError(PS_ERR_BAD_PARAMETER_VALUE, false,
    209                 "Unable to find TEMP.IMAGE, TEMP.MASK and TEMP.VARIANCE in recipe");
     78
     79    // Pixel rejection
     80    psTrace("ppStack", 1, "Reject pixels....\n");
     81    if (!ppStackReject(options, config)) {
     82        psError(PS_ERR_UNKNOWN, false, "Unable to reject pixels.");
     83        psFree(stack);
     84        psFree(options);
    21085        return false;
    21186    }
     87    psLogMsg("ppStack", PS_LOG_INFO, "Stage 5: Pixel Rejection: %f sec", psTimerClear("PPSTACK_STEPS"));
     88    ppStackMemDump("reject");
    21289
    213     float threshold = psMetadataLookupF32(NULL, recipe, "THRESHOLD.MASK"); // Threshold for mask deconvolution
    214     float imageRej = psMetadataLookupF32(NULL, recipe, "IMAGE.REJ"); // Maximum fraction of image to reject
    215                                                                      // before rejecting entire image
    216     float poorFrac = psMetadataLookupF32(&mdok, recipe, "POOR.FRACTION"); // Fraction for "poor"
    217 
    218     const char *statsName = psMetadataLookupStr(&mdok, config->arguments, "STATS"); // Filename for statistics
    219     psMetadata *stats = NULL;           // Container for statistics
    220     FILE *statsFile = NULL;             // File stream for statistics
    221     if (statsName && strlen(statsName) > 0) {
    222         psString resolved = pmConfigConvertFilename(statsName, config, true, true); // Resolved filename
    223         statsFile = fopen(resolved, "w");
    224         if (!statsFile) {
    225             psError(PS_ERR_IO, true, "Unable to open statistics file %s for writing.\n", resolved);
    226             psFree(resolved);
    227             return false;
    228         } else {
    229             stats = psMetadataAlloc();
    230         }
    231         psFree(resolved);
    232     }
    233 
    234     pmFPAfile *output = psMetadataLookupPtr(NULL, config->files, "PPSTACK.OUTPUT"); // Output file
    235     if (!output) {
    236         psError(PS_ERR_UNEXPECTED_NULL, false, "Can't find output data!");
    237         return false;
    238     }
    239     int num = psMetadataLookupS32(NULL, config->arguments, "INPUTS.NUM"); // Number of inputs
    240 
    241     psMetadata *ppsub = psMetadataLookupMetadata(NULL, config->recipes, "PPSUB"); // PPSUB recipe
    242     int overlap = 2 * psMetadataLookupS32(NULL, ppsub,
    243                                           "KERNEL.SIZE"); // Overlap by kernel size between consecutive scans
    244 
    245     if (!pmConfigMaskSetBits(NULL, NULL, config)) {
    246         psError(PS_ERR_UNKNOWN, false, "Unable to determine mask value.");
    247         return false;
    248     }
    249 
    250     memDump("start");
    251 
    252     psLogMsg("ppStack", PS_LOG_INFO, "Stage 0 : Initialization and Configuration : %f sec", psTimerClear("PPSTACK_STEPS"));
    253 
    254     // Preparation iteration: Load the sources, and get a target PSF model
    255     psTrace("ppStack", 1, "Determining target PSF....\n");
    256     psArray *sourceLists = psArrayAlloc(num); // Individual lists of sources for matching
    257     pmPSF *targetPSF = NULL;            // Target PSF
    258     float sumExposure = NAN;            // Sum of exposure times
    259     psVector *inputMask = psVectorAlloc(num, PS_TYPE_VECTOR_MASK); // Mask for inputs
    260     psVectorInit(inputMask, 0);
    261     if (psMetadataLookupBool(NULL, config->arguments, "HAVE.PSF")) {
    262         pmFPAfileActivate(config->files, false, NULL);
    263         fileActivation(config, prepareFiles, true);
    264         pmFPAview *view = filesIterateDown(config);
    265         if (!view) {
    266             return false;
    267         }
    268 
    269         // Generate list of PSFs
    270         psMetadataIterator *fileIter = psMetadataIteratorAlloc(config->files, PS_LIST_HEAD,
    271                                                                "^PPSTACK.INPUT$"); // Iterator
    272         psMetadataItem *fileItem; // Item from iteration
    273         psArray *psfs = psArrayAlloc(num); // PSFs for PSF envelope
    274         int numCols = 0, numRows = 0;   // Size of image
    275         int index = 0;                  // Index for file
    276         while ((fileItem = psMetadataGetAndIncrement(fileIter))) {
    277             assert(fileItem->type == PS_DATA_UNKNOWN);
    278             pmFPAfile *inputFile = fileItem->data.V; // An input file
    279             pmChip *chip = pmFPAviewThisChip(view, inputFile->fpa); // The chip: holds the PSF
    280             pmPSF *psf = psMetadataLookupPtr(NULL, chip->analysis, "PSPHOT.PSF"); // PSF
    281             if (!psf) {
    282                 psError(PS_ERR_UNKNOWN, false, "Unable to find PSF.");
    283                 psFree(view);
    284                 psFree(sourceLists);
    285                 psFree(fileIter);
    286                 psFree(psfs);
    287                 psFree(inputMask);
    288                 return false;
    289             }
    290             psfs->data[index] = psMemIncrRefCounter(psf);
    291             psMetadataRemoveKey(chip->analysis, "PSPHOT.PSF");
    292 
    293             pmCell *cell = pmFPAviewThisCell(view, inputFile->fpa); // Cell of interest
    294             pmHDU *hdu = pmHDUFromCell(cell);
    295             assert(hdu && hdu->header);
    296             int naxis1 = psMetadataLookupS32(NULL, hdu->header, "NAXIS1"); // Number of columns
    297             int naxis2 = psMetadataLookupS32(NULL, hdu->header, "NAXIS2"); // Number of rows
    298             if (naxis1 <= 0 || naxis2 <= 0) {
    299                 psError(PS_ERR_UNKNOWN, false, "Unable to determine size of image from PSF.");
    300                 psFree(view);
    301                 psFree(sourceLists);
    302                 psFree(fileIter);
    303                 psFree(psfs);
    304                 psFree(inputMask);
    305                 return false;
    306             }
    307             if (numCols == 0 && numRows == 0) {
    308                 numCols = naxis1;
    309                 numRows = naxis2;
    310             }
    311 
    312             pmReadout *ro = pmFPAviewThisReadout(view, inputFile->fpa); // Readout with sources
    313             psArray *sources = psMetadataLookupPtr(NULL, ro->analysis, "PSPHOT.SOURCES"); // Sources
    314             if (!sources) {
    315                 psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find sources in readout.");
    316                 return NULL;
    317             }
    318             sourceLists->data[index] = psMemIncrRefCounter(sources);
    319 
    320             // Re-do photometry if we don't trust the source lists
    321             if (psMetadataLookupBool(NULL, recipe, "PHOT")) {
    322                 psTrace("ppStack", 2, "Photometering input %d of %d....\n", index, num);
    323                 pmFPAfileActivate(config->files, false, NULL);
    324                 fileActivationSingle(config, convolveFiles, true, index);
    325                 pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPSTACK.INPUT", index); // File
    326                 pmFPAview *view = filesIterateDown(config);
    327                 if (!view) {
    328                     psFree(sourceLists);
    329                     psFree(targetPSF);
    330                     psFree(inputMask);
    331                    return false;
    332                 }
    333 
    334                 pmReadout *ro = pmFPAviewThisReadout(view, file->fpa); // Readout of interest
    335 
    336                 if (!ppStackInputPhotometry(ro, sources, config)) {
    337                     psError(PS_ERR_UNKNOWN, false, "Unable to do photometry on input sources");
    338                     psFree(sourceLists);
    339                     psFree(targetPSF);
    340                     psFree(inputMask);
    341                     return false;
    342                 }
    343 
    344                 psFree(view);
    345                 if (!filesIterateUp(config)) {
    346                     psFree(sourceLists);
    347                     psFree(targetPSF);
    348                     psFree(inputMask);
    349                     return false;
    350                 }
    351                 pmFPAfileActivate(config->files, false, NULL);
    352                 fileActivation(config, prepareFiles, true);
    353             }
    354 
    355             index++;
    356         }
    357         psFree(fileIter);
    358 
    359         // Zero point calibration
    360         sumExposure = ppStackSourcesTransparency(sourceLists, inputMask, view, config);
    361         if (!isfinite(sumExposure) || sumExposure <= 0) {
    362             psError(PS_ERR_UNKNOWN, false, "Unable to calculate transparency differences");
    363             psFree(sourceLists);
    364             psFree(targetPSF);
    365             psFree(inputMask);
    366             return false;
    367         }
    368 
    369         // Generate target PSF
    370         targetPSF = ppStackPSF(config, numCols, numRows, psfs, inputMask);
    371         psFree(psfs);
    372         if (!targetPSF) {
    373             psError(PS_ERR_UNKNOWN, false, "Unable to determine output PSF.");
    374             psFree(sourceLists);
    375             psFree(view);
    376             psFree(inputMask);
    377             return false;
    378         }
    379         psMetadataAddPtr(config->arguments, PS_LIST_TAIL, "PSF.TARGET", PS_DATA_UNKNOWN,
    380                          "Target PSF for stack", targetPSF);
    381 
    382         pmChip *outChip = pmFPAviewThisChip(view, output->fpa); // Output chip
    383         psMetadataAddPtr(outChip->analysis, PS_LIST_TAIL, "PSPHOT.PSF", PS_DATA_UNKNOWN,
    384                          "Target PSF", targetPSF);
    385         outChip->data_exists = true;
    386 
    387         psFree(view);
    388         if (!filesIterateUp(config)) {
    389             return false;
    390         }
    391     }
    392 
    393     psLogMsg("ppStack", PS_LOG_INFO, "Stage 1 : Load Sources and Generate Target PSF : %f sec", psTimerClear("PPSTACK_STEPS"));
    394     memDump("psf");
    395 
    396     psArray *imageNames = psArrayAlloc(num);
    397     psArray *maskNames = psArrayAlloc(num);
    398     psArray *varianceNames = psArrayAlloc(num);
    399     for (int i = 0; i < num; i++) {
    400         psString imageName = NULL, maskName = NULL, varianceName = NULL; // Names for convolved images
    401         psStringAppend(&imageName, "%s/%s.%d.%s", tempDir, tempName, i, tempImage);
    402         psStringAppend(&maskName, "%s/%s.%d.%s", tempDir, tempName, i, tempMask);
    403         psStringAppend(&varianceName, "%s/%s.%d.%s", tempDir, tempName, i, tempVariance);
    404         psTrace("ppStack", 5, "Temporary files: %s %s %s\n", imageName, maskName, varianceName);
    405         imageNames->data[i] = imageName;
    406         maskNames->data[i] = maskName;
    407         varianceNames->data[i] = varianceName;
    408     }
    409     // Free the outputName that we grabbed above to set up tempName.
    410     psFree(outputName);
    411 
    412     // Generate convolutions and write them to disk
    413     psTrace("ppStack", 1, "Convolving inputs to target PSF....\n");
    414     psArray *cells = psArrayAlloc(num); // Cells for convolved images --- a handle for reading again
    415     psArray *subKernels = psArrayAlloc(num); // Subtraction kernels --- required in the stacking
    416     psArray *subRegions = psArrayAlloc(num); // Subtraction regions --- required in the stacking
    417     psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
    418     int numGood = 0;                    // Number of good frames
    419     int numCols = 0, numRows = 0;       // Size of image
    420     psVector *matchChi2 = psVectorAlloc(num, PS_TYPE_F32); // chi^2 for stamps when matching
    421     psVectorInit(matchChi2, NAN);
    422     psVector *weightings = psVectorAlloc(num, PS_TYPE_F32); // Combination weightings for images (1/noise^2)
    423     psVectorInit(weightings, NAN);
    424     psList *fpaList = psListAlloc(NULL); // List of input FPAs, for concept averaging
    425     psList *cellList = psListAlloc(NULL); // List of input cells, for concept averaging
    426     psArray *covariances = psArrayAlloc(num); // Covariance matrices
    427     for (int i = 0; i < num; i++) {
    428         if (inputMask->data.U8[i]) {
    429             continue;
    430         }
    431         psTrace("ppStack", 2, "Convolving input %d of %d to target PSF....\n", i, num);
    432         pmFPAfileActivate(config->files, false, NULL);
    433         fileActivationSingle(config, convolveFiles, true, i);
    434         pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPSTACK.INPUT", i); // File of interest
    435         pmFPAview *view = filesIterateDown(config);
    436         if (!view) {
    437             psFree(sourceLists);
    438             psFree(targetPSF);
    439             psFree(rng);
    440             psFree(inputMask);
    441             psFree(matchChi2);
    442             psFree(fpaList);
    443             psFree(cellList);
    444             psFree(covariances);
    445             return false;
    446         }
    447 
    448         pmReadout *readout = pmFPAviewThisReadout(view, file->fpa); // Input readout
    449         psFree(view);
    450 
    451         if (numCols == 0 && numRows == 0) {
    452             numCols = readout->image->numCols;
    453             numRows = readout->image->numRows;
    454         } else if (numCols != readout->image->numCols || numRows != readout->image->numRows) {
    455             psError(PS_ERR_UNKNOWN, true, "Sizes of input images don't match: %dx%d vs %dx%d",
    456                     readout->image->numCols, readout->image->numRows, numCols, numRows);
    457             psFree(sourceLists);
    458             psFree(targetPSF);
    459             psFree(rng);
    460             psFree(inputMask);
    461             psFree(matchChi2);
    462             psFree(fpaList);
    463             psFree(cellList);
    464             psFree(covariances);
    465             return false;
    466         }
    467 
    468         // Background subtraction, scaling and normalisation is performed automatically by the image matching
    469         psArray *regions = NULL, *kernels = NULL; // Regions and kernels used in subtraction
    470         psTimerStart("PPSTACK_MATCH");
    471 
    472         if (!ppStackMatch(readout, &regions, &kernels, &matchChi2->data.F32[i], &weightings->data.F32[i],
    473                           sourceLists->data[i], targetPSF, rng, config)) {
    474             psErrorStackPrint(stderr, "Unable to match image %d --- ignoring.", i);
    475             inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = PPSTACK_MASK_MATCH;
    476             psErrorClear();
    477             continue;
    478         }
    479         covariances->data[i] = psMemIncrRefCounter(readout->covariance);
    480 
    481         if (stats) {
    482             pmSubtractionKernels *kernels = psMetadataLookupPtr(NULL, readout->analysis,
    483                                                                 PM_SUBTRACTION_ANALYSIS_KERNEL);// Conv kernel
    484             psMetadataAddF32(stats, PS_LIST_TAIL, "TIME_MATCH", PS_META_DUPLICATE_OK,
    485                              "Time to match PSF", psTimerMark("PPSTACK_MATCH"));
    486             psMetadataAddF32(stats, PS_LIST_TAIL, "STAMP.MEAN", PS_META_DUPLICATE_OK,
    487                              "Mean deviation for stamps", kernels->mean);
    488             psMetadataAddF32(stats, PS_LIST_TAIL, "STAMP.RMS", PS_META_DUPLICATE_OK,
    489                              "RMS deviation for stamps", kernels->rms);
    490             psMetadataAddF32(stats, PS_LIST_TAIL, "STAMP.NUM", PS_META_DUPLICATE_OK,
    491                              "Number of stamps", kernels->numStamps);
    492             float deconv = psMetadataLookupF32(NULL, readout->analysis,
    493                                                PM_SUBTRACTION_ANALYSIS_DECONV_MAX); // Deconvolution fraction
    494             psMetadataAddF32(stats, PS_LIST_TAIL, "KERNEL.DECONV", PS_META_DUPLICATE_OK,
    495                              "Deconvolution fraction for kernel", deconv);
    496             psMetadataAddF32(stats, PS_LIST_TAIL, "PPSTACK.WEIGHTING", PS_META_DUPLICATE_OK,
    497                              "Weighting for image", weightings->data.F32[i]);
    498         }
    499         psLogMsg("ppStack", PS_LOG_INFO, "Time to match image %d: %f sec", i, psTimerClear("PPSTACK_MATCH"));
    500 
    501         subRegions->data[i] = regions;
    502         subKernels->data[i] = kernels;
    503 
    504         // Write the temporary convolved files
    505         pmHDU *hdu = readout->parent->parent->parent->hdu; // HDU for convolved image
    506         assert(hdu);
    507         writeImage(imageNames->data[i], hdu->header, readout->image, config);
    508         psMetadata *maskHeader = psMetadataCopy(NULL, hdu->header); // Copy of header, for mask
    509         pmConfigMaskWriteHeader(config, maskHeader);
    510         writeImage(maskNames->data[i], maskHeader, readout->mask, config);
    511         psFree(maskHeader);
    512         psImageCovarianceTransfer(readout->variance, readout->covariance);
    513         writeImage(varianceNames->data[i], hdu->header, readout->variance, config);
    514 #ifdef TESTING
    515         {
    516             psString name = NULL;
    517             psStringAppend(&name, "covariance_%d.fits", i);
    518             writeImage(name, hdu->header, readout->covariance->image, config);
    519             pmStackVisualPlotTestImage(readout->covariance->image, name);
    520             psFree(name);
    521         }
    522 #endif
    523 
    524         pmCell *inCell = readout->parent; // Input cell
    525 
    526         psListAdd(cellList, PS_LIST_TAIL, inCell);
    527         psListAdd(fpaList, PS_LIST_TAIL, inCell->parent->parent);
    528 
    529         cells->data[i] = psMemIncrRefCounter(inCell);
    530         if (!filesIterateUp(config)) {
    531             psFree(fpaList);
    532             psFree(cellList);
    533             psFree(covariances);
    534             return false;
    535         }
    536         numGood++;
    537 
    538         memDump("match");
    539     }
    540     psFree(sourceLists);
    541     psFree(targetPSF);
    542     psFree(rng);
    543 
    544     psLogMsg("ppStack", PS_LOG_INFO, "Stage 2 : Generate Convolutions and Save : %f sec", psTimerClear("PPSTACK_STEPS"));
    545 
    546     if (numGood == 0) {
    547         psError(PS_ERR_UNKNOWN, false, "No good images to combine.");
    548         psFree(subKernels);
    549         psFree(subRegions);
    550         psFree(cells);
    551         psFree(inputMask);
    552         psFree(matchChi2);
    553         psFree(fpaList);
    554         psFree(cellList);
    555         psFree(covariances);
    556         return false;
    557     }
    558 
    559 
    560     // Update concepts for output
    561     {
    562         pmFPAview view;                 // View for output
    563         view.chip = view.cell = view.readout = 0;
    564         pmCell *outCell = pmFPAfileThisCell(config->files, &view, "PPSTACK.OUTPUT"); // Output cell
    565         pmFPA *outFPA = outCell->parent->parent; // Output FPA
    566         pmConceptsAverageFPAs(outFPA, fpaList);
    567         pmConceptsAverageCells(outCell, cellList, NULL, NULL, true);
    568         psFree(fpaList);
    569         psFree(cellList);
    570 
    571         // Update the value of a concept
    572 #define UPDATE_CONCEPT(SOURCE, NAME, VALUE) { \
    573             psMetadataItem *item = psMetadataLookup(SOURCE->concepts, NAME); \
    574             psAssert(item, "Concept should be present"); \
    575             psAssert(item->type == PS_TYPE_F32, "Concept should be F32"); \
    576             item->data.F32 = VALUE; \
    577         }
    578 
    579         UPDATE_CONCEPT(outFPA,  "FPA.EXPOSURE",  sumExposure);
    580         UPDATE_CONCEPT(outCell, "CELL.EXPOSURE", sumExposure);
    581         UPDATE_CONCEPT(outCell, "CELL.DARKTIME", NAN);
    582     }
    583 
    584 
    585     // Reject images out-of-hand on the basis of their match chi^2
    586     {
    587         psVector *values = psVectorAllocEmpty(num, PS_TYPE_F32); // Values to sort
    588         for (int i = 0; i < num; i++) {
    589             if (inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PPSTACK_MASK_ALL) {
    590                 continue;
    591             }
    592             values->data.F32[values->n++] = matchChi2->data.F32[i];
    593         }
    594         assert(values->n == numGood);
    595         if (!psVectorSortInPlace(values)) {
    596             psError(PS_ERR_UNKNOWN, false, "Unable to sort vector.");
    597             psFree(subKernels);
    598             psFree(subRegions);
    599             psFree(cells);
    600             psFree(inputMask);
    601             psFree(matchChi2);
    602             psFree(values);
    603             psFree(covariances);
    604             return false;
    605         }
    606         float median = numGood % 2 ? values->data.F32[numGood / 2] :
    607             0.5 * (values->data.F32[numGood / 2 - 1] + values->data.F32[numGood / 2]);
    608 
    609         float rms = 0.74 * (values->data.F32[numGood * 3 / 4] -
    610                             values->data.F32[numGood / 4]); // Estimated RMS from interquartile range
    611 
    612         psFree(values);
    613 
    614         float rej = psMetadataLookupF32(NULL, recipe, "MATCH.REJ"); // Rejection threshold (stdevs)
    615         if (isfinite(rej)) {
    616             float thresh = median + rej * rms; // Threshold for rejection
    617             psLogMsg("ppStack", PS_LOG_INFO, "chi^2 rejection threshold = %f + %f * %f = %f",
    618                      median, rej, rms, thresh);
    619 
    620             int numRej = 0;                 // Number rejected
    621             numGood = 0;                    // Number of good images
    622             for (int i = 0; i < num; i++) {
    623               if (inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PPSTACK_MASK_ALL) {
    624                     continue;
    625                 }
    626                 if (matchChi2->data.F32[i] > thresh) {
    627                     numRej++;
    628                     inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i] |= PPSTACK_MASK_CHI2;
    629                     psLogMsg("ppStack", PS_LOG_INFO, "Rejecting image %d because of large matching chi^2: %f",
    630                              i, matchChi2->data.F32[i]);
    631                 } else {
    632                     psLogMsg("ppStack", PS_LOG_INFO, "Image %d has matching chi^2: %f",
    633                              i, matchChi2->data.F32[i]);
    634                     numGood++;
    635                 }
    636             }
    637         }
    638     }
    639 
    640     if (numGood == 0) {
    641         psError(PS_ERR_UNKNOWN, false, "No good images to combine.");
    642         psFree(subKernels);
    643         psFree(subRegions);
    644         psFree(cells);
    645         psFree(inputMask);
    646         psFree(matchChi2);
    647         psFree(covariances);
    648         return false;
    649     }
    650 
    651 #ifdef TESTING
    652     psTraceSetLevel("psModules.imcombine", 7);
    653 #endif
    654 
    655     psLogMsg("ppStack", PS_LOG_INFO, "Stage 3 : Basic Image Rejection  : %f sec", psTimerClear("PPSTACK_STEPS"));
    656 
    657     // Start threading
    658     ppStackThreadInit();
    659     ppStackThreadData *stack = ppStackThreadDataSetup(cells, imageNames, maskNames, varianceNames,
    660                                                       covariances, config);
    661     if (!stack) {
    662         psError(PS_ERR_IO, false, "Unable to initialise stack threads.");
    663         psFree(subKernels);
    664         psFree(subRegions);
    665         psFree(inputMask);
    666         psFree(matchChi2);
    667         psFree(cells);
    668         psFree(covariances);
    669         return false;
    670     }
    671 
    672     psTimerStart("PPSTACK_INITIAL");
    673 
    674     memDump("preinitial");
    675 
    676     // Stack the convolved files
    677     psTrace("ppStack", 1, "Initial stack of convolved images....\n");
    678     pmReadout *outRO = NULL;            // Output readout
    679     pmFPAview *view = NULL;             // View to readout
    680     psArray *inspect = NULL;            // Array of arrays of pixels to inspect
    681     {
    682         int row0, col0;                 // Offset for readout
    683         int numCols, numRows;           // Size of readout
    684         ppStackThread *thread = stack->threads->data[0]; // Representative thread
    685         if (!pmReadoutStackSetOutputSize(&col0, &row0, &numCols, &numRows, thread->readouts)) {
    686             psError(PS_ERR_UNKNOWN, false, "problem setting output readout size.");
    687             psFree(subKernels);
    688             psFree(subRegions);
    689             psFree(stack);
    690             psFree(inputMask);
    691             psFree(matchChi2);
    692             psFree(cells);
    693             psFree(covariances);
    694             return false;
    695         }
    696 
    697         pmFPAfileActivate(config->files, false, NULL);
    698         fileActivation(config, combineFiles, true);
    699         view = filesIterateDown(config);
    700         if (!view) {
    701             psFree(subKernels);
    702             psFree(subRegions);
    703             psFree(stack);
    704             psFree(inputMask);
    705             psFree(matchChi2);
    706             psFree(cells);
    707             psFree(covariances);
    708             return false;
    709         }
    710 
    711         pmCell *outCell = pmFPAfileThisCell(config->files, view, "PPSTACK.OUTPUT"); // Output cell
    712         outRO = pmReadoutAlloc(outCell); // Output readout
    713 
    714         psString maskBadStr = psMetadataLookupStr(NULL, recipe, "MASK.BAD"); // Name of bits to mask for bad
    715         psImageMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
    716         if (!pmReadoutStackDefineOutput(outRO, col0, row0, numCols, numRows, true, true, maskBad)) {
    717             psError(PS_ERR_UNKNOWN, false, "Unable to prepare output.");
    718             psFree(subKernels);
    719             psFree(subRegions);
    720             psFree(stack);
    721             psFree(inputMask);
    722             psFree(matchChi2);
    723             psFree(view);
    724             psFree(outRO);
    725             psFree(cells);
    726             psFree(covariances);
    727             return false;
    728         }
    729 
    730         psFree(cells);
    731 
    732         bool status;                    // Status of read
    733         int numChunk;                   // Number of chunks
    734         for (numChunk = 0; true; numChunk++) {
    735             ppStackThread *thread = ppStackThreadRead(&status, stack, config, numChunk, overlap);
    736             if (!status) {
    737                 // Something went wrong
    738                 psError(PS_ERR_UNKNOWN, false, "Unable to read chunk %d", numChunk);
    739                 psFree(subKernels);
    740                 psFree(subRegions);
    741                 psFree(stack);
    742                 psFree(inputMask);
    743                 psFree(matchChi2);
    744                 psFree(view);
    745                 psFree(outRO);
    746                 psFree(covariances);
    747                 return false;
    748             }
    749             if (!thread) {
    750                 // Nothing more to read
    751                 break;
    752             }
    753 
    754             // call: ppStackReadoutInitial(config, outRO, readouts, subRegions, subKernels)
    755             psThreadJob *job = psThreadJobAlloc("PPSTACK_INITIAL_COMBINE"); // Job to start
    756             psArrayAdd(job->args, 1, thread);
    757             psArrayAdd(job->args, 1, config);
    758             psArrayAdd(job->args, 1, outRO);
    759             psArrayAdd(job->args, 1, inputMask);
    760             psArrayAdd(job->args, 1, weightings);
    761             psArrayAdd(job->args, 1, matchChi2);
    762             if (!psThreadJobAddPending(job)) {
    763                 psFree(job);
    764                 psFree(subKernels);
    765                 psFree(subRegions);
    766                 psFree(stack);
    767                 psFree(inputMask);
    768                 psFree(weightings);
    769                 psFree(matchChi2);
    770                 psFree(view);
    771                 psFree(outRO);
    772                 psFree(covariances);
    773                 return false;
    774             }
    775             psFree(job);
    776         }
    777 
    778         if (!psThreadPoolWait(false)) {
    779             psError(PS_ERR_UNKNOWN, false, "Unable to do initial combination.");
    780             psFree(subKernels);
    781             psFree(subRegions);
    782             psFree(stack);
    783             psFree(inputMask);
    784             psFree(matchChi2);
    785             psFree(view);
    786             psFree(outRO);
    787             psFree(covariances);
    788             return false;
    789         }
    790 
    791         // Harvest the jobs, gathering the inspection lists
    792         inspect = psArrayAlloc(num);
    793         for (int i = 0; i < num; i++) {
    794             if (inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
    795                 continue;
    796             }
    797             inspect->data[i] = psArrayAllocEmpty(numChunk);
    798         }
    799         psThreadJob *job;               // Completed job
    800         while ((job = psThreadJobGetDone())) {
    801             psAssert(strcmp(job->type, "PPSTACK_INITIAL_COMBINE") == 0,
    802                      "Job has incorrect type: %s", job->type);
    803             psArray *results = job->results; // Results of job
    804             for (int i = 0; i < num; i++) {
    805                 if (inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
    806                     continue;
    807                 }
    808                 inspect->data[i] = psArrayAdd(inspect->data[i], 1, results->data[i]);
    809             }
    810             psFree(job);
    811         }
    812 
    813         memDump("initial");
    814     }
    815 
    816 #ifdef TESTING
    817     writeImage("combined_initial.fits", NULL, outRO->image, config);
    818     pmStackVisualPlotTestImage(outRO->image, "combined_initial.fits");
    819 #endif
    820 
    821     if (stats) {
    822         psMetadataAddF32(stats, PS_LIST_TAIL, "TIME_INITIAL", 0,
    823                          "Time to make initial stack", psTimerMark("PPSTACK_INITIAL"));
    824     }
    825     psLogMsg("ppStack", PS_LOG_INFO, "Stage 4 : Make Initial Stack : %f sec", psTimerClear("PPSTACK_STEPS"));
    826     psLogMsg("ppStack", PS_LOG_INFO, "Time to make initial stack: %f sec", psTimerClear("PPSTACK_INITIAL"));
    827 
    828     psTimerStart("PPSTACK_REJECT");
    829 
    830     // Pixel rejection
    831     psArray *rejected = psArrayAlloc(num);
    832     {
    833         int numRejected = 0;        // Number of inputs rejected completely
    834 
    835         // Count images rejected out of hand
    836         for (int i = 0; i < num; i++) {
    837             if (inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
    838                 numRejected++;
    839             }
    840         }
    841 
    842         for (int i = 0; i < num; i++) {
    843             if (inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
    844                 continue;
    845             }
    846 
    847             psThreadJob *job = psThreadJobAlloc("PPSTACK_INSPECT"); // Job to start
    848             psArrayAdd(job->args, 1, inspect);
    849             PS_ARRAY_ADD_SCALAR(job->args, i, PS_TYPE_S32);
    850             if (!psThreadJobAddPending(job)) {
    851                 psFree(job);
    852                 psFree(subKernels);
    853                 psFree(subRegions);
    854                 psFree(stack);
    855                 psFree(inputMask);
    856                 psFree(view);
    857                 psFree(outRO);
    858                 psFree(inspect);
    859                 psFree(rejected);
    860                 psFree(covariances);
    861                 psFree(matchChi2);
    862                 return false;
    863             }
    864             psFree(job);
    865         }
    866 
    867         if (!psThreadPoolWait(true)) {
    868             psError(PS_ERR_UNKNOWN, false, "Unable to concatenate inspection lists.");
    869             psFree(subKernels);
    870             psFree(subRegions);
    871             psFree(stack);
    872             psFree(inputMask);
    873             psFree(view);
    874             psFree(outRO);
    875             psFree(inspect);
    876             psFree(rejected);
    877             psFree(covariances);
    878             psFree(matchChi2);
    879             return false;
    880         }
    881 
    882         if (psMetadataLookupS32(NULL, config->arguments, "-threads") > 0) {
    883             pmStackRejectThreadsInit();
    884         }
    885 
    886         int stride = psMetadataLookupS32(NULL, ppsub, "STRIDE"); // Size of convolution patches
    887 
    888         // Reject bad pixels
    889         for (int i = 0; i < num; i++) {
    890             if (inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
    891                 continue;
    892             }
    893             psTimerStart("PPSTACK_REJECT");
    894 
    895 #ifdef TESTING
    896             {
    897                 psImage *mask = psPixelsToMask(NULL, inspect->data[i],
    898                                                psRegionSet(0, numCols - 1, 0, numRows - 1),
    899                                                0xff); // Mask image
    900                 psString name = NULL;           // Name of image
    901                 psStringAppend(&name, "inspect_%03d.fits", i);
    902                 pmStackVisualPlotTestImage(mask, name);
    903                 psFits *fits = psFitsOpen(name, "w");
    904                 psFree(name);
    905                 psFitsWriteImage(fits, NULL, mask, 0, NULL);
    906                 psFree(mask);
    907                 psFitsClose(fits);
    908             }
    909 #endif
    910 
    911             psPixels *reject = pmStackReject(inspect->data[i], numCols, numRows, threshold, poorFrac, stride,
    912                                              subRegions->data[i], subKernels->data[i]); // Rejected pixels
    913 
    914 #ifdef TESTING
    915             {
    916                 psImage *mask = psPixelsToMask(NULL, reject, psRegionSet(0, numCols - 1, 0, numRows - 1),
    917                                                0xff); // Mask image
    918                 psString name = NULL;           // Name of image
    919                 psStringAppend(&name, "reject_%03d.fits", i);
    920                 pmStackVisualPlotTestImage(mask, name);
    921                 psFits *fits = psFitsOpen(name, "w");
    922                 psFree(name);
    923                 psFitsWriteImage(fits, NULL, mask, 0, NULL);
    924                 psFree(mask);
    925                 psFitsClose(fits);
    926             }
    927 #endif
    928 
    929             psFree(inspect->data[i]);
    930             inspect->data[i] = NULL;
    931 
    932             if (!reject) {
    933                 psWarning("Rejection on image %d didn't work --- reject entire image.", i);
    934                 numRejected++;
    935                 inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = PPSTACK_MASK_REJECT;
    936             } else {
    937                 float frac = reject->n / (float)(numCols * numRows); // Pixel fraction
    938                 psLogMsg("ppStack", PS_LOG_INFO, "%ld pixels rejected from image %d (%.1f%%)",
    939                          reject->n, i, frac * 100.0);
    940                 if (frac > imageRej) {
    941                     psWarning("Image %d rejected completely because rejection fraction (%.3f) "
    942                               "exceeds limit (%.3f)", i, frac, imageRej);
    943                     psFree(reject);
    944                     // reject == NULL means reject image completely
    945                     reject = NULL;
    946                     inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = PPSTACK_MASK_BAD;
    947                     numRejected++;
    948                 }
    949             }
    950 
    951             // Images without a list of rejected pixels (the list may be empty) are rejected completely
    952             rejected->data[i] = reject;
    953 
    954             if (stats) {
    955                 psMetadataAddF32(stats, PS_LIST_TAIL, "TIME_REJECT", PS_META_DUPLICATE_OK,
    956                                  "Time to perform rejection", psTimerMark("PPSTACK_REJECT"));
    957                 psMetadataAddS32(stats, PS_LIST_TAIL, "REJECT_PIXELS", PS_META_DUPLICATE_OK,
    958                                  "Number of pixels rejected", reject ? reject->n : 0);
    959             }
    960             psLogMsg("ppStack", PS_LOG_INFO, "Time to perform rejection on image %d: %f sec", i,
    961                      psTimerClear("PPSTACK_REJECT"));
    962         }
    963 
    964         psFree(inspect);
    965         psFree(subKernels);
    966         psFree(subRegions);
    967 
    968         if (numRejected >= num - 1) {
    969             psError(PS_ERR_UNKNOWN, true, "All inputs completely rejected; unable to proceed.");
    970             psFree(stack);
    971             psFree(rejected);
    972             psFree(inputMask);
    973             psFree(view);
    974             psFree(outRO);
    975             psFree(covariances);
    976             psFree(matchChi2);
    977             return false;
    978         }
    979 
    980         if (stats) {
    981             psMetadataAddS32(stats, PS_LIST_TAIL, "REJECT_IMAGES", 0,
    982                              "Number of images rejected completely", numRejected);
    983         }
    984     }
    985 
    986     psLogMsg("ppStack", PS_LOG_INFO, "Stage 5 : Pixel Rejection : %f sec", psTimerClear("PPSTACK_STEPS"));
    987     psTimerStart("PPSTACK_FINAL");
    988 
    989     memDump("reject");
    99090
    99191    // Final combination
    99292    psTrace("ppStack", 2, "Final stack of convolved images....\n");
    993     {
    994         stack->lastScan = 0;            // Reset read
    995         bool status;                    // Status of read
    996         for (int numChunk = 0; true; numChunk++) {
    997             ppStackThread *thread = ppStackThreadRead(&status, stack, config, numChunk, 0);
    998             if (!status) {
    999                 // Something went wrong
    1000                 psError(PS_ERR_UNKNOWN, false, "Unable to read chunk %d", numChunk);
    1001                 psFree(stack);
    1002                 psFree(rejected);
    1003                 psFree(inputMask);
    1004                 psFree(view);
    1005                 psFree(outRO);
    1006                 psFree(covariances);
    1007                 psFree(matchChi2);
    1008                 return false;
    1009             }
    1010             if (!thread) {
    1011                 // Nothing more to read
    1012                 break;
    1013             }
     93    if (!ppStackCombineFinal(stack, options, config)) {
     94        psError(PS_ERR_UNKNOWN, false, "Unable to perform final combination.");
     95        psFree(stack);
     96        psFree(options);
     97        return false;
     98    }
     99    psLogMsg("ppStack", PS_LOG_INFO, "Stage 6: Final Stack: %f sec", psTimerClear("PPSTACK_STEPS"));
     100    ppStackMemDump("final");
    1014101
    1015             // call: ppStackReadoutFinal(config, outRO, readouts, rejected)
    1016             psThreadJob *job = psThreadJobAlloc("PPSTACK_FINAL_COMBINE"); // Job to start
    1017             psArrayAdd(job->args, 1, thread);
    1018             psArrayAdd(job->args, 1, config);
    1019             psArrayAdd(job->args, 1, outRO);
    1020             psArrayAdd(job->args, 1, inputMask);
    1021             psArrayAdd(job->args, 1, rejected);
    1022             psArrayAdd(job->args, 1, weightings);
    1023             psArrayAdd(job->args, 1, matchChi2);
    1024             if (!psThreadJobAddPending(job)) {
    1025                 psFree(job);
    1026                 psFree(stack);
    1027                 psFree(rejected);
    1028                 psFree(inputMask);
    1029                 psFree(view);
    1030                 psFree(outRO);
    1031                 psFree(covariances);
    1032                 psFree(matchChi2);
    1033                 return false;
    1034             }
    1035             psFree(job);
    1036         }
    1037102
    1038         if (!psThreadPoolWait(true)) {
    1039             psError(PS_ERR_UNKNOWN, false, "Unable to do final combination.");
    1040             psFree(stack);
    1041             psFree(inputMask);
    1042             psFree(rejected);
    1043             psFree(view);
    1044             psFree(outRO);
    1045             psFree(covariances);
    1046             psFree(matchChi2);
    1047             return false;
    1048         }
     103    // Clean up
     104    psTrace("ppStack", 2, "Cleaning up after combination....\n");
     105    if (!ppStackCombineFinal(stack, options, config)) {
     106        psError(PS_ERR_UNKNOWN, false, "Unable to clean up.");
     107        psFree(stack);
     108        psFree(options);
     109        return false;
    1049110    }
     111    psLogMsg("ppStack", PS_LOG_INFO, "Stage 7: WCS & JPEGS: %f sec", psTimerClear("PPSTACK_STEPS"));
     112    ppStackMemDump("cleanup");
    1050113
    1051     memDump("final");
    1052 
    1053 #ifdef TESTING
    1054     pmStackVisualPlotTestImage(outRO->image, "combined_initial.fits");
    1055     writeImage("combined_final.fits", NULL, outRO->image, config);
    1056 #endif
    1057 
    1058     // Sum covariance matrices
    1059     for (int i = 0; i < num; i++) {
    1060         if (inputMask->data.U8[i]) {
    1061             psFree(covariances->data[i]);
    1062             covariances->data[i] = NULL;
    1063         }
    1064     }
    1065     outRO->covariance = psImageCovarianceSum(covariances);
    1066     psFree(covariances);
    1067     psFree(matchChi2);
    1068 
    1069     if (stats) {
    1070         psMetadataAddF32(stats, PS_LIST_TAIL, "TIME_FINAL", 0,
    1071                          "Time to make final stack", psTimerMark("PPSTACK_FINAL"));
    1072     }
    1073 
    1074     psLogMsg("ppStack", PS_LOG_INFO, "Stage 6 : Final Stack : %f sec", psTimerClear("PPSTACK_STEPS"));
    1075     psLogMsg("ppStack", PS_LOG_INFO, "Time to make final stack: %f sec", psTimerClear("PPSTACK_FINAL"));
    1076 
    1077     psTrace("ppStack", 2, "Cleaning up after combination....\n");
    1078 
    1079 #if 0
    1080     // Ensure masked regions really look masked
    1081     {
    1082         psString maskBadStr = psMetadataLookupStr(NULL, recipe, "MASK.BAD"); // Name of bits for bad
    1083         psImageMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
    1084         if (!pmReadoutMaskApply(outRO, maskBad)) {
    1085             psWarning("Unable to apply mask");
    1086         }
    1087     }
    1088 #endif
    1089 
    1090     // Close up
    1091     bool wcsDone = false;           // Have we done the WCS?
    1092     for (int i = 0; i < num; i++) {
    1093         if (inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
    1094             continue;
    1095         }
    1096 
    1097         ppStackThread *thread = stack->threads->data[0]; // Representative stack
    1098         pmReadout *inRO = thread->readouts->data[i]; // Template readout
    1099         if (inRO && !wcsDone) {
    1100             // Copy astrometry over
    1101             wcsDone = true;
    1102             pmHDU *inHDU = pmHDUFromCell(inRO->parent); // Template HDU
    1103             pmHDU *outHDU = pmHDUFromCell(outRO->parent); // Output HDU
    1104             pmChip *outChip = outRO->parent->parent; // Output chip
    1105             pmFPA *outFPA = outChip->parent; // Output FPA
    1106             if (!outHDU || !inHDU) {
    1107                 psWarning("Unable to find HDU at FPA level to copy astrometry.");
    1108             } else {
    1109                 if (!pmAstromReadWCS(outFPA, outChip, inHDU->header, 1.0)) {
    1110                     psErrorClear();
    1111                     psWarning("Unable to read WCS astrometry from input FPA.");
    1112                     wcsDone = false;
    1113                 } else {
    1114                     if (!outHDU->header) {
    1115                         outHDU->header = psMetadataAlloc();
    1116                     }
    1117                     if (!pmAstromWriteWCS(outHDU->header, outFPA, outChip, WCS_TOLERANCE)) {
    1118                         psErrorClear();
    1119                         psWarning("Unable to write WCS astrometry to output FPA.");
    1120                         wcsDone = false;
    1121                     }
    1122                 }
    1123             }
    1124         }
    1125 
    1126         if (tempDelete) {
    1127             psString imageResolved = pmConfigConvertFilename(imageNames->data[i], config, false, false);
    1128             psString maskResolved = pmConfigConvertFilename(maskNames->data[i], config, false, false);
    1129             psString varianceResolved = pmConfigConvertFilename(varianceNames->data[i], config, false, false);
    1130             if (unlink(imageResolved) == -1 || unlink(maskResolved) == -1 ||
    1131                 unlink(varianceResolved) == -1) {
    1132                 psWarning("Unable to delete temporary files for image %d", i);
    1133             }
    1134             psFree(imageResolved);
    1135             psFree(maskResolved);
    1136             psFree(varianceResolved);
    1137         }
    1138     }
    1139     psFree(imageNames);
    1140     psFree(maskNames);
    1141     psFree(varianceNames);
    1142 
    1143     psFree(inputMask);
    1144114    psFree(stack);
    1145115
    1146     memDump("cleanup");
    1147116
    1148     // Generate binned JPEGs
    1149     {
    1150         int bin1 = psMetadataLookupS32(NULL, recipe, "BIN1"); // First binning level
    1151         int bin2 = psMetadataLookupS32(NULL, recipe, "BIN2"); // Second binning level
    1152 
    1153         // Target cells
    1154         pmCell *cell1 = pmFPAfileThisCell(config->files, view, "PPSTACK.OUTPUT.JPEG1");
    1155         pmCell *cell2 = pmFPAfileThisCell(config->files, view, "PPSTACK.OUTPUT.JPEG2");
    1156         psImageMaskType maskValue = pmConfigMaskGet("BLANK", config); // Bits to mask
    1157 
    1158         pmReadout *ro1 = pmReadoutAlloc(cell1), *ro2 = pmReadoutAlloc(cell2); // Binned readouts
    1159         if (!pmReadoutRebin(ro1, outRO, maskValue, bin1, bin1) || !pmReadoutRebin(ro2, ro1, 0, bin2, bin2)) {
    1160             psError(PS_ERR_UNKNOWN, false, "Unable to bin output.");
    1161             psFree(ro1);
    1162             psFree(ro2);
    1163             psFree(outRO);
    1164             return false;
    1165         }
    1166         psFree(ro1);
    1167         psFree(ro2);
    1168     }
    1169 
    1170     psLogMsg("ppStack", PS_LOG_INFO, "Stage 7 : WCS & JPEGS : %f sec", psTimerClear("PPSTACK_STEPS"));
    1171 
    1172     if (psMetadataLookupBool(&mdok, recipe, "PHOTOMETRY")) {
    1173         psTrace("ppStack", 1, "Photometering stacked image....\n");
    1174 
    1175         psTimerStart("PPSTACK_PHOT");
    1176 
    1177         fileActivation(config, combineFiles, false);
    1178         fileActivation(config, photFiles, true);
    1179         pmFPAview *photView = filesIterateDown(config);
    1180         fileActivation(config, combineFiles, true);
    1181 
    1182         if (!ppStackPhotometry(config, outRO, photView)) {
    1183             psError(PS_ERR_UNKNOWN, false, "Unable to perform photometry on output.");
    1184             filesIterateUp(config);
    1185             psFree(outRO);
    1186             psFree(photView);
    1187             return false;
    1188         }
    1189         psFree(photView);
    1190 
    1191         if (stats) {
    1192             pmFPAfile *photFile = psMetadataLookupPtr(NULL, config->files, "PSPHOT.INPUT"); // File
    1193             pmReadout *photRO = pmFPAviewThisReadout(view, photFile->fpa); // Readout with the sources
    1194             psArray *sources = psMetadataLookupPtr(NULL, photRO->analysis, "PSPHOT.SOURCES"); // Sources
    1195             psMetadataAddS32(stats, PS_LIST_TAIL, "NUM_SOURCES", 0, "Number of sources detected", sources->n);
    1196             psMetadataAddF32(stats, PS_LIST_TAIL, "TIME_PHOT", 0,
    1197                              "Time to do photometry", psTimerMark("PPSTACK_PHOT"));
    1198         }
    1199         psLogMsg("ppStack", PS_LOG_INFO, "Time to do photometry: %f sec", psTimerClear("PPSTACK_PHOT"));
    1200     }
    1201 
    1202     psLogMsg("ppStack", PS_LOG_INFO, "Stage 8 : Photometry Analysis : %f sec", psTimerClear("PPSTACK_STEPS"));
    1203 
    1204     psThreadPoolFinalize();
    1205 
    1206     memDump("phot");
    1207 
    1208     // Statistics on output
    1209     if (stats) {
    1210         psTrace("ppStack", 1, "Gathering statistics on stacked image....\n");
    1211         psString maskBadStr = psMetadataLookupStr(NULL, recipe, "MASK.BAD"); // Name of bits for bad
    1212         psImageMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
    1213 
    1214         ppStatsFPA(stats, outRO->parent->parent->parent, view, maskBad, config);
    1215     }
    1216 
    1217     memDump("stats");
    1218 
    1219     // Put version information into the header
    1220     pmHDU *hdu = pmHDUFromCell(outRO->parent);
    1221     if (!hdu) {
    1222         psError(PS_ERR_UNKNOWN, false, "Unable to find HDU for output.");
    1223         psFree(outRO);
    1224         psFree(view);
     117    // Photometry
     118    psTrace("ppStack", 1, "Photometering stacked image....\n");
     119    if (!ppStackPhotometry(options, config)) {
     120        psError(PS_ERR_UNKNOWN, false, "Unable to perform photometry.");
     121        psFree(options);
    1225122        return false;
    1226123    }
    1227     if (!hdu->header) {
    1228         hdu->header = psMetadataAlloc();
    1229     }
    1230     ppStackVersionHeader(hdu->header);
     124    psLogMsg("ppStack", PS_LOG_INFO, "Stage 8: Photometry Analysis: %f sec", psTimerClear("PPSTACK_STEPS"));
     125    ppStackMemDump("photometry");
    1231126
    1232     psFree(outRO);
    1233     psFree(view);
    1234127
    1235     // Write out summary statistics
    1236     if (stats) {
    1237         psMetadataAddF32(stats, PS_LIST_TAIL, "TIME_STACK", 0,
    1238                          "Time to do photometry", psTimerClear("PPSTACK_TOTAL"));
    1239 
    1240         const char *statsMDC = psMetadataConfigFormat(stats);
    1241         if (!statsMDC || strlen(statsMDC) == 0) {
    1242             psError(PS_ERR_IO, false, "Unable to get statistics MDC file.\n");
    1243         } else {
    1244             fprintf(statsFile, "%s", statsMDC);
    1245         }
    1246         psFree((void *)statsMDC);
    1247         fclose(statsFile);
    1248 
    1249         psFree(stats);
    1250     }
    1251 
    1252     // Write out the output files
    1253     if (!filesIterateUp(config)) {
     128    // Finish up
     129    psTrace("ppStack", 1, "Finishing up....\n");
     130    if (!ppStackFinish(options, config)) {
     131        psError(PS_ERR_UNKNOWN, false, "Unable to finish up.");
     132        psFree(options);
    1254133        return false;
    1255134    }
     135    psLogMsg("ppStack", PS_LOG_INFO, "Stage 9: Final output: %f sec", psTimerClear("PPSTACK_STEPS"));
     136    ppStackMemDump("finish");
    1256137
    1257     psLogMsg("ppStack", PS_LOG_INFO, "Stage 9 : Final Output : %f sec", psTimerClear("PPSTACK_STEPS"));
    1258 
    1259     memDump("finish");
    1260 
     138    psFree(options);
    1261139    return true;
    1262140}
  • trunk/ppStack/src/ppStackPhotometry.c

    r23259 r23341  
    99
    1010#include "ppStack.h"
     11#include "ppStackLoop.h"
    1112
    12 #define PHOT_SOURCE_MASK (PM_SOURCE_MODE_FAIL | PM_SOURCE_MODE_SATSTAR | PM_SOURCE_MODE_BLEND | \
    13                           PM_SOURCE_MODE_BADPSF | PM_SOURCE_MODE_DEFECT | PM_SOURCE_MODE_SATURATED | \
    14                           PM_SOURCE_MODE_CR_LIMIT | PM_SOURCE_MODE_EXT_LIMIT) // Mask to apply to sources
    15 
    16 bool ppStackInputPhotometry(const pmReadout *ro, const psArray *sources, const pmConfig *config)
     13bool ppStackPhotometry(ppStackOptions *options, pmConfig *config)
    1714{
    18     PM_ASSERT_READOUT_NON_NULL(ro, false);
    19     PS_ASSERT_ARRAY_NON_NULL(sources, false);
     15    psAssert(options, "Require options");
     16    psAssert(config, "Require configuration");
    2017
    2118    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
     
    2320
    2421    bool mdok;                          // Status of MD lookup
    25 
    26     float zpRadius = psMetadataLookupS32(&mdok, recipe, "PHOT.RADIUS"); // Radius for PHOT measurement
    27     if (!mdok) {
    28         psError(PS_ERR_UNKNOWN, true, "Unable to find PHOT.RADIUS in recipe");
    29         return false;
    30     }
    31     float zpSigma = psMetadataLookupF32(&mdok, recipe, "PHOT.SIGMA"); // Gaussian sigma for photometry
    32     if (!mdok) {
    33         psError(PS_ERR_UNKNOWN, true, "Unable to find PHOT.SIGMA in recipe");
    34         return false;
    35     }
    36     float zpFrac = psMetadataLookupF32(&mdok, recipe, "PHOT.FRAC"); // Fraction of good pixels for photometry
    37     if (!mdok) {
    38         psError(PS_ERR_UNKNOWN, true, "Unable to find PHOT.FRAC in recipe");
    39         return false;
     22    if (!psMetadataLookupBool(&mdok, recipe, "PHOTOMETRY")) {
     23        // Nothing to do
     24        return true;
    4025    }
    4126
    42     psString maskValStr = psMetadataLookupStr(&mdok, recipe, "MASK.VAL"); // Name of bits to mask going in
    43     if (!mdok || !maskValStr) {
    44         psError(PS_ERR_UNKNOWN, false, "Unable to find MASK.VAL in recipe");
    45         return false;
    46     }
    47     psImageMaskType maskVal = pmConfigMaskGet(maskValStr, config); // Bits to mask
     27    psTimerStart("PPSTACK_PHOT");
    4828
    49     psImage *image = ro->image, *mask = ro->mask; // Image and mask from readout
     29    ppStackFileActivation(config, PPSTACK_FILES_COMBINE, false);
     30    ppStackFileActivation(config, PPSTACK_FILES_PHOT, true);
     31    pmFPAview *photView = ppStackFilesIterateDown(config); // View to readout
     32    ppStackFileActivation(config, PPSTACK_FILES_COMBINE, true);
    5033
    51     // Measure background
    52     psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
    53     psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV); // Statistics
    54     if (!psImageBackground(stats, NULL, image, mask, maskVal, rng)) {
    55         psError(PS_ERR_UNKNOWN, false, "Unable to measure background for image");
    56         psFree(stats);
    57         psFree(rng);
    58         return false;
    59     }
    60     float bg = stats->robustMedian; // Background level
    61     psFree(stats);
    62     psFree(rng);
    63 
    64     // Photometer sources
    65     int numCols = image->numCols, numRows = image->numRows; // Size of image
    66     int numSources = sources->n; // Number of sources
    67     int numGood = 0;            // Number of good sources
    68     float maxMag = -INFINITY;   // Maximum magnitude
    69     for (int j = 0; j < numSources; j++) {
    70         pmSource *source = sources->data[j]; // Source of interest
    71         if (source->mode & PHOT_SOURCE_MASK || !isfinite(source->psfMag)) {
    72             source->psfMag = NAN;
    73             continue;
    74         }
    75         float x = source->peak->xf, y = source->peak->yf; // Coordinates of source
    76         int xCentral = x + 0.5, yCentral = y + 0.5; // Central pixel
    77         // Range of integration
    78         int xMin = PS_MAX(0, xCentral - zpRadius), xMax = PS_MIN(numCols - 1, xCentral + zpRadius);
    79         int yMin = PS_MAX(0, yCentral - zpRadius), yMax = PS_MIN(numRows - 1, yCentral + zpRadius);
    80 
    81         // Integrate
    82         double sumImage = 0.0, sumKernel = 0.0; // Sums from integration
    83         int numBadPix = 0;         // Number of bad pixels
    84         for (int v = yMin; v <= yMax; v++) {
    85             float dy2 = PS_SQR(y - v); // Distance from centroid
    86             for (int u = xMin; u <= xMax; u++) {
    87                 if (mask->data.PS_TYPE_IMAGE_MASK_DATA[v][u] & maskVal) {
    88                     numBadPix++;
    89                     continue;
    90                 }
    91                 float dx2 = PS_SQR(x - u); // Distance from centroid
    92                 double kernel = exp(-0.5 * (dx2 + dy2) / PS_SQR(zpSigma)); // Kernel value
    93                 sumImage += (image->data.F32[v][u] - bg) * kernel;
    94                 sumKernel += kernel;
    95             }
    96         }
    97 
    98         if (numBadPix > zpFrac * M_PI * PS_SQR(zpRadius) || !isfinite(sumImage)) {
    99             source->psfMag = NAN;
    100         } else {
    101             source->psfMag = - 2.5 * log10(sumImage / sumKernel * M_PI * PS_SQR(zpRadius));
    102             if (isfinite(source->psfMag)) {
    103                 numGood++;
    104                 maxMag = PS_MAX(maxMag, source->psfMag);
    105             }
    106         }
    107     }
    108     psLogMsg("ppStack", PS_LOG_INFO, "Photometered %d good sources in image; max mag %f", numGood, maxMag);
    109 
    110     return true;
    111 }
    112 
    113 
    114 
    115 bool ppStackPhotometry(pmConfig *config, const pmReadout *readout, const pmFPAview *view)
    116 {
    117     pmFPAfile *photFile = psMetadataLookupPtr(NULL, config->files, "PSPHOT.INPUT");
    118     pmFPACopy(photFile->fpa, readout->parent->parent->parent);
     34    pmFPAfile *photFile = psMetadataLookupPtr(NULL, config->files, "PSPHOT.INPUT"); // File for photometry
     35    pmFPACopy(photFile->fpa, options->outRO->parent->parent->parent);
    11936
    12037    if (psMetadataLookupBool(NULL, config->arguments, "-visual")) {
     
    12239    }
    12340
     41#if 0
    12442    // Need to ensure aperture residual is not calculated
    12543    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PSPHOT_RECIPE); // Recipe
     
    13149        item->data.B = false;
    13250    }
     51#endif
    13352
    13453    // set maskValue and markValue in the psphot recipe
    13554    psImageMaskType maskValue = pmConfigMaskGet("BLANK", config); // Bits to mask
    13655    psImageMaskType markValue = pmConfigMaskGet("MARK.VALUE", config); // Bits to use for marking
    137     psMetadataAddImageMask (recipe, PS_LIST_TAIL, "MASK.PSPHOT", PS_META_REPLACE, "Bits to mask", maskValue);
    138     psMetadataAddImageMask (recipe, PS_LIST_TAIL, "MARK.PSPHOT", PS_META_REPLACE, "Bits to use for mark", markValue);
     56    psMetadataAddImageMask(recipe, PS_LIST_TAIL, "MASK.PSPHOT", PS_META_REPLACE,
     57                            "Bits to mask", maskValue);
     58    psMetadataAddImageMask(recipe, PS_LIST_TAIL, "MARK.PSPHOT", PS_META_REPLACE,
     59                           "Bits to use for mark", markValue);
    13960
    140     // XXX replace with psphotReadoutKnownSources (config, view)
    141     // XXX this function requires that we construct the input source list and place it on PSPHOT.INPUT.CMF
    142     pmCell *sourcesCell = pmFPAfileThisCell(config->files, view, "PPSTACK.OUTPUT");
    143     psArray *inSources = psMetadataLookupPtr (NULL, sourcesCell->analysis, "PSPHOT.SOURCES");
     61    pmCell *sourcesCell = pmFPAfileThisCell(config->files, photView, "PPSTACK.OUTPUT");
     62    psArray *inSources = psMetadataLookupPtr(NULL, sourcesCell->analysis, "PSPHOT.SOURCES");
    14463    if (!inSources) {
    14564        psError(PS_ERR_UNKNOWN, false, "Unable to find input sources");
     65        psFree(photView);
    14666        return false;
    14767    }
    14868
    149     if (!psphotReadoutKnownSources(config, view, inSources)) {
     69    if (!psphotReadoutKnownSources(config, photView, inSources)) {
    15070        // Clear the error, so that the output files are written.
    15171        psError(PS_ERR_UNKNOWN, false, "Unable to perform photometry on stacked image.");
     72        psFree(photView);
    15273        return false;
    15374    }
     75    psFree(photView);
    15476
    15577    if (!pmFPAfileDropInternal(config->files, "PSPHOT.BACKMDL") ||
     
    16284    pmFPAfileActivate(config->files, false, "PSPHOT.INPUT");
    16385
     86    if (options->stats) {
     87        pmReadout *photRO = pmFPAviewThisReadout(photView, photFile->fpa); // Readout with the sources
     88        psArray *sources = psMetadataLookupPtr(NULL, photRO->analysis, "PSPHOT.SOURCES"); // Sources
     89        psMetadataAddS32(options->stats, PS_LIST_TAIL, "NUM_SOURCES", 0,
     90                         "Number of sources detected", sources->n);
     91        psMetadataAddF32(options->stats, PS_LIST_TAIL, "TIME_PHOT", 0,
     92                         "Time to do photometry", psTimerMark("PPSTACK_PHOT"));
     93    }
     94    psLogMsg("ppStack", PS_LOG_INFO, "Time to do photometry: %f sec", psTimerClear("PPSTACK_PHOT"));
     95
    16496    return true;
    16597}
  • trunk/ppStack/src/ppStackReadout.c

    r21477 r23341  
    88#include <psphot.h>
    99
     10#include "ppStackThread.h"
    1011#include "ppStack.h"
    1112
     
    1617    psArray *args = job->args;          // Arguments
    1718    ppStackThread *thread = args->data[0]; // Thread
    18     pmConfig *config = args->data[1];   // Configuration
    19     pmReadout *outRO = args->data[2];   // Output readout
    20     psVector *mask = args->data[3];     // Mask for inputs
    21     psVector *weightings = args->data[4]; // Weightings (1/noise^2) for each image
    22     psVector *addVariance = args->data[5]; // Additional variance when rejecting
     19    ppStackOptions *options = args->data[1]; // Options
     20    pmConfig *config = args->data[2];   // Configuration
     21
     22    pmReadout *outRO = options->outRO;  // Output readout
     23    psVector *mask = options->inputMask; // Mask for inputs
     24    psVector *weightings = options->weightings; // Weightings (1/noise^2) for each image
     25    psVector *addVariance = options->matchChi2; // Additional variance when rejecting
    2326
    2427    psArray *inspect = ppStackReadoutInitial(config, outRO, thread->readouts, mask,
     
    3740    psArray *args = job->args;          // Arguments
    3841    ppStackThread *thread = args->data[0]; // Thread
    39     pmConfig *config = args->data[1];   // Configuration
    40     pmReadout *outRO = args->data[2];   // Output readout
    41     psVector *mask = args->data[3];     // Mask for inputs
    42     psArray *rejected = args->data[4];  // Rejected pixels
    43     psVector *weightings = args->data[5];  // Weighting (1/noise^2) for each image
    44     psVector *addVariance = args->data[6];  // Weighting (1/noise^2) for each image
     42    ppStackOptions *options = args->data[1]; // Options
     43    pmConfig *config = args->data[2];   // Configuration
     44
     45    pmReadout *outRO = options->outRO;  // Output readout
     46    psVector *mask = options->inputMask; // Mask for inputs
     47    psArray *rejected = options->rejected; // Rejected pixels
     48    psVector *weightings = options->weightings; // Weightings (1/noise^2) for each image
     49    psVector *addVariance = options->matchChi2; // Additional variance when rejecting
    4550
    4651    bool status = ppStackReadoutFinal(config, outRO, thread->readouts, mask, rejected,
  • trunk/ppStack/src/ppStackThread.c

    r23190 r23341  
    99
    1010#include "ppStack.h"
     11#include "ppStackOptions.h"
     12#include "ppStackThread.h"
     13
    1114
    1215#define THREAD_WAIT 10000               // Microseconds to wait if thread is not available
     
    5356}
    5457
    55 ppStackThreadData *ppStackThreadDataSetup(const psArray *cells, const psArray *imageNames,
    56                                           const psArray *maskNames, const psArray *varianceNames,
    57                                           const psArray *covariances, const pmConfig *config)
    58 {
     58ppStackThreadData *ppStackThreadDataSetup(const ppStackOptions *options, const pmConfig *config)
     59{
     60    psAssert(options, "Require options");
     61    psAssert(config, "Require configuration");
     62
     63    const psArray *cells = options->cells; // Array of input cells
     64    const psArray *imageNames = options->imageNames; // Names of images to read
     65    const psArray *maskNames = options->maskNames; // Names of masks to read
     66    const psArray *varianceNames = options->varianceNames; // Names of variance maps to read
     67    const psArray *covariances = options->covariances; // Covariance matrices (already read)
     68
    5969    PS_ASSERT_ARRAY_NON_NULL(cells, NULL);
    6070    PS_ASSERT_ARRAYS_SIZE_EQUAL(cells, imageNames, NULL);
     
    245255
    246256    {
    247         psThreadTask *task = psThreadTaskAlloc("PPSTACK_INITIAL_COMBINE", 6);
     257        psThreadTask *task = psThreadTaskAlloc("PPSTACK_INITIAL_COMBINE", 3);
    248258        task->function = &ppStackReadoutInitialThread;
    249259        psThreadTaskAdd(task);
     
    259269
    260270    {
    261         psThreadTask *task = psThreadTaskAlloc("PPSTACK_FINAL_COMBINE", 7);
     271        psThreadTask *task = psThreadTaskAlloc("PPSTACK_FINAL_COMBINE", 3);
    262272        task->function = &ppStackReadoutFinalThread;
    263273        psThreadTaskAdd(task);
Note: See TracChangeset for help on using the changeset viewer.