IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Changeset 27876


Ignore:
Timestamp:
May 6, 2010, 8:02:38 PM (16 years ago)
Author:
eugene
Message:

working on psphotStack

Location:
branches/eam_branches/psphot.20100506
Files:
2 added
10 edited

Legend:

Unmodified
Added
Removed
  • branches/eam_branches/psphot.20100506/doc/stack.txt

    r27848 r27876  
     1
     220100506:
     3
     4  we may load RAW and/or CNV images.
     5
     6  * options->convolve:
     7    * if only one of RAW or CNV exists, convolve it to match target PSF
     8    * if both exist, convolve desired one
     9   
     10    * if matching PSF exists, use it (unless told to re-measure)
     11   
     12    * output goes to OUT (which is then used by psphot analysis routines)
     13
     14  * detect
     15
     16    * if (RAW) ? RAW : OUT
     17   
     18  *   
     19
    120
    22120100503:
  • branches/eam_branches/psphot.20100506/src/Makefile.am

    r27819 r27876  
    9595        psphotFitSourcesLinearStack.c \
    9696        psphotSourceMatch.c           \
     97        psphotStackMatchPSFs.c       \
     98        psphotStackMatchPSFsUtils.c  \
    9799        psphotCleanup.c
    98100
  • branches/eam_branches/psphot.20100506/src/psphot.h

    r27819 r27876  
    367367int pmPhotObjSortByX (const void **a, const void **b);
    368368
     369/// Options for stacking process
     370typedef struct {
     371    // Setup
     372   
     373    int num;                            // Number of inputs
     374    bool convolve;                      // Convolve images?
     375    // psArray *convImages, *convMasks, *convVariances; // Filenames for the temporary convolved images
     376
     377    // bool matchZPs;                      // Adjust relative fluxes based on transparency analysis?
     378    // bool photometry;                    // Perform photometry?
     379    // psMetadata *stats;                  // Statistics for output
     380    // FILE *statsFile;                    // File to which to write statistics
     381    // psArray *origImages, *origMasks, *origVariances; // Filenames of the original images
     382    // psArray *origCovars;                // Original covariances matrices
     383    // int quality;                        // Bad data quality flag
     384
     385    // Prepare
     386    pmPSF *psf;                         // Target PSF
     387    psVector *inputSeeing;              // Input seeing FWHMs
     388    psVector *inputMask;                // Mask for inputs
     389
     390    float targetSeeing;                 // Target seeing FWHM
     391    psArray *sourceLists;               // Individual lists of sources for matching
     392    psVector *norm;                     // Normalisation for each image
     393    psArray *psfs;
     394
     395    // psVector *exposures;                // Exposure times
     396    // float sumExposure;                  // Sum of exposure times
     397    // float zp;                           // Zero point for output
     398    // psVector *inputMask;                // Mask for inputs
     399    // psArray *sources;                   // Matched sources
     400
     401    // Convolve
     402    psArray *kernels;                   // PSF-matching kernels --- required in the stacking
     403    psArray *regions;                   // PSF-matching regions --- required in the stacking
     404    psVector *matchChi2;                // chi^2 for stamps from matching
     405    psVector *weightings;               // Combination weightings for images (1/noise^2)
     406    // psArray *cells;                     // Cells for convolved images --- a handle for reading again
     407    // int numCols, numRows;               // Size of image
     408    // psArray *convCovars;                // Convolved covariance matrices
     409
     410    // Combine initial
     411    // pmReadout *outRO;                   // Output readout
     412    // pmReadout *expRO;                   // Exposure readout
     413    // psArray *inspect;                   // Array of arrays of pixels to inspect
     414
     415    // Rejection
     416    // psArray *rejected;                  // Rejected pixels
     417} psphotStackOptions;
     418
     419/*** psphotStackMatchPSF prototypes ***/
     420bool psphotStackMatchPSFs (pmConfig *config, const pmFPAview *view);
     421bool psphotStackMatchPSFsReadout (pmConfig *config, const pmFPAview *view, psphotStackOptions *options, int index);
     422
     423// psphotStackMatchPSFsUtils
     424psVector *SetOptWidths (bool *optimum, psMetadata *recipe);
     425pmReadout *makeFakeReadout(pmConfig *config, pmReadout *raw, psArray *sources, pmPSF *psf, psImageMaskType maskVal, int fullSize);
     426bool rescaleData(pmReadout *readout, pmConfig *config, psphotStackOptions *options, int index);
     427bool renormKernel(pmReadout *readout, psphotStackOptions *options, int index);
     428bool saveMatchData (pmReadout *readout, psphotStackOptions *options, int index);
     429bool matchKernel(pmConfig *config, pmReadout *cnv, pmReadout *raw, psphotStackOptions *options, int index);
     430bool dumpImageDiff(pmReadout *readoutConv, pmReadout *readoutFake, pmReadout *readoutRef, int index, char *rootname);
     431bool dumpImage(pmReadout *readoutOut, pmReadout *readoutRef, int index, char *rootname);
     432bool loadKernel (pmConfig *config, pmReadout *readoutCnv, psphotStackOptions *options, int index);
     433bool stackRenormaliseReadout(const pmConfig *config, pmReadout *readout);
     434psImage *stackBackgroundModel(pmReadout *ro, const pmConfig *config);
     435psArray *stackSourcesFilter(psArray *sources, int exclusion);
     436void coordsFromSource(float *x, float *y, const pmSource *source);
     437bool readImage(psImage **target, const char *name, const pmConfig *config);
     438
    369439#endif
  • branches/eam_branches/psphot.20100506/src/psphotErrorCodes.dat

    r27002 r27876  
    1111APERTURE                Problem with aperture photometry
    1212SKY                     Problem in sky determination
     13IO                      Problem in data I/O
    1314# these errors correspond to standard exit conditions
    1415ARGUMENTS               Incorrect arguments
  • branches/eam_branches/psphot.20100506/src/psphotSetMaskBits.c

    r21254 r27876  
    3737    return true;
    3838}
     39
     40// XXX should these be in config->analysis or somewhere else besides 'recipe'?
  • branches/eam_branches/psphot.20100506/src/psphotStackArguments.c

    r27657 r27876  
    1111    if (psArgumentGet(argc, argv, "-help")) writeHelpInfo(stdout);
    1212    if (psArgumentGet(argc, argv, "-h")) writeHelpInfo(stdout);
     13
     14    if (psArgumentGet(argc, argv, "-template")) dumpTemplate();
    1315
    1416    // load config data from default locations
     
    8486          "\n"
    8587          "where INPUTS.mdc contains various METADATAs, each with:\n"
    86           "\tIMAGE(STR):     Image filename\n"
    87           "\tMASK(STR):      Mask filename\n"
    88           "\tVARIANCE(STR):  Variance map filename\n"
     88          "\tIMAGE    : Image filename\n"
     89          "\tMASK     : Mask filename\n"
     90          "\tVARIANCE : Variance map filename\n"
     91          "(use -template to generate a sample input.mdc file)\n"
    8992          "OUTROOT is the 'root name' for output files\n"
    9093          "\n"
     
    144147}
    145148
     149static void dumpTemplate(void) {
     150
     151    fprintf (stdout, "# this line is required for multiple INPUT blocks to be accepted\n");
     152    fprintf (stdout, "INPUT MULTI\n\n");
     153
     154    fprintf (stdout, "# copy and repeat the following block as needed (one per input image set)\n");
     155    fprintf (stdout, "# either RAW (unconvolved) or CNV (convolved) input images are required\n");
     156    fprintf (stdout, "# if both are supplied, by default RAW is used for detection, CNV is convolved (further) to match target PSF\n");
     157    fprintf (stdout, "# if MASK or VARIANCE images are not supplied, they will be generated\n");
     158    fprintf (stdout, "# if MASK or VARIANCE images are not supplied, they will be generated\n");
     159    fprintf (stdout, "# PSF may be supplied for the convolution target\n");
     160    fprintf (stdout, "INPUT METADATA\n");
     161    fprintf (stdout, "  RAW:IMAGE     STR   file.im.fits   # signal image filename\n");
     162    fprintf (stdout, "  RAW:MASK      STR   file.mk.fits   # mask image filename\n");
     163    fprintf (stdout, "  RAW:VARIANCE  STR   file.wt.fits   # variance image filename\n");
     164    fprintf (stdout, "  RAW:PSF       STR   file.psf.fits  # psf from input unconvolved image\n");
     165
     166    fprintf (stdout, "  CNV:IMAGE     STR   file.im.fits   # signal image filename\n");
     167    fprintf (stdout, "  CNV:MASK      STR   file.mk.fits   # mask image filename\n");
     168    fprintf (stdout, "  CNV:VARIANCE  STR   file.wt.fits   # variance image filename\n");
     169    fprintf (stdout, "  CNV:PSF       STR   file.psf.fits  # psf from input convolved image\n");
     170
     171    fprintf (stdout, "  SOURCES       STR   file.cmf       # measured source positions\n");
     172    fprintf (stdout, "END\n");
     173
     174    psLibFinalize();
     175    exit(PS_EXIT_SUCCESS);
     176}
  • branches/eam_branches/psphot.20100506/src/psphotStackImageLoop.c

    r27848 r27876  
    1414    pmReadout *readout;
    1515
    16     pmFPAfile *input = psMetadataLookupPtr (&status, config->files, "PSPHOT.INPUT");
    17     if (!status) {
     16    pmFPAfile *inputRaw = psMetadataLookupPtr (&status, config->files, "PSPHOT.STACK.INPUT.RAW");
     17    pmFPAfile *inputCnv = psMetadataLookupPtr (&status, config->files, "PSPHOT.STACK.INPUT.CNV");
     18
     19    pmFPAfile *input = inputRaw ? inputRaw : inputCnv;
     20
     21    if (!input) {
    1822        psError(PSPHOT_ERR_PROG, false, "Can't find input data!");
    1923        return false;
     
    2933        psLogMsg ("psphot", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
    3034        if (! chip->process || ! chip->file_exists) { continue; }
    31         if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE ("failed input for Chip in psphotStack.");
     35        // if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE ("failed input for Chip in psphotStack.");
    3236
    3337        // there is now only a single chip (multiple readouts?). loop over it and process
    3438        while ((cell = pmFPAviewNextCell (view, input->fpa, 1)) != NULL) {
    3539            psLogMsg ("psphot", 5, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
    36             if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE ("failed input for Cell in psphotStack.");
     40            // if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE ("failed input for Cell in psphotStack.");
    3741
    3842            // process each of the readouts
     
    4145                if (! readout->data_exists) { continue; }
    4246
    43 # if (0)               
    44                 // uncomment to generate matched psfs
     47                // PSF matching
    4548                if (!psphotStackMatchPSFs (config, view)) {
    4649                    psError(psErrorCodeLast(), false, "failure in psphotStackMatchPSFs for chip %d, cell %d, readout %d\n", view->chip, view->cell, view->readout);
     
    4851                    return false;
    4952                }
    50 # endif
    5153
    5254                // XXX for now, we assume there is only a single chip in the PHU:
  • branches/eam_branches/psphot.20100506/src/psphotStackMatchPSFs.c

    r27850 r27876  
    11# include "psphotInternal.h"
    22
    3 bool psphotStackMatchPSFs (pmConfig *config, const pmFPAview *view, bool firstPass)
     3bool psphotStackMatchPSFs (pmConfig *config, const pmFPAview *view)
    44{
    55    bool status = true;
     6
     7    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, "PSPHOT");
     8    psAssert(recipe, "We've thrown an error on this before.");
    69
    710    int num = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.INPUT.NUM");
    811    psAssert (status, "programming error: must define PSPHOT.INPUT.NUM");
    912
    10     // loop over the available readouts
     13    // 'options' carries info needed to perform the stack matching
     14    psphotStackOptions *options = psphotStackOptionsAlloc(num);
     15
     16    options->convolve = psMetadataLookupBool (&status, recipe, "PSPHOT.STACK.MATCH.PSF");
     17    psAssert (status, "PSPHOT.STACK.MATCH.PSF not in recipe");
     18
     19    if (options->convolve) {
     20        char *convolveSource = psMetadataLookupStr (&status, recipe, "PSPHOT.STACK.MATCH.PSF.SOURCE");
     21        options->convolveSource = psphotStackConvolveSourceFromString (convolveSource);
     22        if (options->convolveSource == PSPHOT_CNV_SRC_NONE) {
     23            psError (PSPHOT_ERR_CONFIG, true, "stack convolution source not defined in recipe");
     24            return false;
     25        }
     26    }
     27
     28    // loop over the available readouts (ignore chisq image)
    1129    for (int i = 0; i < num; i++) {
    12         if (!psphotStackMatchPSFsReadout (config, view, i)) {
     30        if (!psphotStackMatchPSFsPrepare (config, view, options, i)) {
     31            psError (PSPHOT_ERR_CONFIG, false, "failed to set PSF matching options for entry %d", i);
     32            return false;
     33        }
     34    }
     35
     36    // Generate target PSF
     37    if (options->convolve) {
     38        options->psf = ppStackPSF(config, options->numCols, options->numRows, options->psfs, options->inputMask);
     39        if (!options->psf) {
     40            psError(psErrorCodeLast(), false, "Unable to determine output PSF.");
     41            return false;
     42        }
     43        psMetadataAddPtr(config->arguments, PS_LIST_TAIL, "PSF.TARGET", PS_DATA_UNKNOWN, "Target PSF for stack", options->psf);
     44        options->targetSeeing = pmPSFtoFWHM(options->psf, 0.5 * options->numCols, 0.5 * options->numRows); // FWHM for target
     45        psLogMsg("psphotStack", PS_LOG_INFO, "Target seeing FWHM: %f\n", options->targetSeeing);
     46
     47        // XXX is this needed to supply the psf to psphot??
     48        // pmChip *outChip = pmFPAfileThisChip(config->files, view, "PPSTACK.TARGET.PSF"); // Output chip
     49        // psMetadataAddPtr(outChip->analysis, PS_LIST_TAIL, "PSPHOT.PSF", PS_DATA_UNKNOWN, "Target PSF", options->psf);
     50        // outChip->data_exists = true;
     51    }
     52
     53    // loop over the available readouts (ignore chisq image)
     54    for (int i = 0; i < num; i++) {
     55        if (!psphotStackMatchPSFsReadout (config, view, options, i)) {
    1356            psError (PSPHOT_ERR_CONFIG, false, "failed to find initial detections for PSPHOT.INPUT entry %d", i);
    1457            return false;
     
    1962
    2063// convolve the image to match desired PSF
    21 bool psphotStackMatchPSFsReadout (pmConfig *config, const pmFPAview *view, int index) {
    22 
    23     bool status;
    24     int pass;
    25     float NSIGMA_PEAK = 25.0;
    26     int NMAX = 0;
     64bool psphotStackMatchPSFsReadout (pmConfig *config, const pmFPAview *view, psphotStackOptions *options, int index) {
    2765
    2866    // find the currently selected readout
    29     pmFPAfile *fileRaw = pmFPAfileSelectSingle(config->files, "PSPHOT.INPUT", index); // File of interest
     67    pmFPAfile *fileRaw = pmFPAfileSelectSingle(config->files, "PSPHOT.STACK.INPUT.RAW", index); // File of interest
    3068    psAssert (fileRaw, "missing file?");
    3169
    32     pmFPAfile *fileCnv = pmFPAfileSelectSingle(config->files, "PSPHOT.INPUT.CONV", index); // File of interest
     70    pmFPAfile *fileCnv = pmFPAfileSelectSingle(config->files, "PSPHOT.STACK.INPUT.CNV", index); // File of interest
    3371    psAssert (fileCnv, "missing file?");
    3472
     
    3977    psAssert (readoutCnv, "missing readout?");
    4078
    41     // user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
    42     psImageMaskType maskVal = psMetadataLookupImageMask(&status, recipe, "MASK.PSPHOT"); // Mask value for bad pixels
    43     psAssert (maskVal, "missing mask value?");
    44 
    45     /***** set up recipe options *****/
    46 
    47     psMetadata *stackRecipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
    48     psAssert(stackRecipe, "We've thrown an error on this before.");
    49 
    50     // Look up appropriate values from the ppSub recipe
    51     psMetadata *subRecipe = psMetadataLookupMetadata(NULL, config->recipes, "PPSUB"); // PPSUB recipe
    52     psAssert(subRecipe, "recipe missing");
    53 
    54     int size = psMetadataLookupS32(NULL, subRecipe, "KERNEL.SIZE"); // Kernel half-size
    55 
    56     float deconvLimit = psMetadataLookupF32(NULL, stackRecipe, "DECONV.LIMIT"); // Limit on deconvolution fraction
    57 
    58     psString maskValStr = psMetadataLookupStr(NULL, subRecipe, "MASK.VAL"); // Name of bits to mask going in
    59     psImageMaskType maskVal = pmConfigMaskGet(maskValStr, config); // Bits to mask going in to pmSubtractionMatch
    60     psString maskPoorStr = psMetadataLookupStr(NULL, stackRecipe, "MASK.POOR"); // Name of bits to mask for poor
    61     psImageMaskType maskPoor = pmConfigMaskGet(maskPoorStr, config); // Bits to mask for poor pixels
    62     psString maskBadStr = psMetadataLookupStr(NULL, stackRecipe, "MASK.BAD"); // Name of bits to mask for bad
    63     psImageMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
    64 
    65     bool mdok;                          // Status of MD lookup
    66     float penalty = psMetadataLookupF32(NULL, subRecipe, "PENALTY"); // Penalty for wideness
    67     int threads = psMetadataLookupS32(NULL, config->arguments, "-threads"); // Number of threads
    68 
    69     if (!pmReadoutMaskNonfinite(readout, maskVal)) {
     79    // set NAN pixels to 'SAT'
     80    psImageMaskType maskVal = pmConfigMaskGet("SAT", config);
     81    if (!pmReadoutMaskNonfinite(readoutRaw, maskVal)) {
    7082        psError(psErrorCodeLast(), false, "Unable to mask non-finite pixels in readout.");
    7183        return false;
     
    7486    // Image Matching (PSFs or just flux)
    7587    if (options->convolve) {
    76       // Full match of PSFs
    77         pmReadout *conv = pmReadoutAlloc(NULL); // Conv readout, for holding results temporarily
    78 
    79         // Read previously produced kernel
    80         if (psMetadataLookupBool(NULL, config->arguments, "PPSTACK.DEBUG.STACK")) {
    81           loadKernel();
    82         } else {
    83           matchKernel();
    84         } // !DEBUG.STACK
    85 
    86         saveMatchData();
    87 
    88         saveChiSquare();
    89 
    90         renormKernel();
    91 
    92         // Reject image completely if the maximum deconvolution fraction exceeds the limit
    93         float deconv = psMetadataLookupF32(NULL, conv->analysis, PM_SUBTRACTION_ANALYSIS_DECONV_MAX); // Max deconvolution fraction
    94         if (deconv > deconvLimit) {
    95             psWarning("Maximum deconvolution fraction (%f) exceeds limit (%f) --- rejecting image %d\n", deconv, deconvLimit, index);
    96             psFree(conv);
    97             return NULL;
    98         }
    99 
    100         readout->analysis = psMetadataCopy(readout->analysis, conv->analysis);
    101 
    102         psFree(conv);
     88        matchKernel(config, readoutCnv, readoutRaw, options, index);
     89        saveMatchData(readoutCnv, options, index);
     90        // renormKernel(readoutCnv, options, index);
    10391    } else {
    104         // only match the flux
    105         float norm = powf(10.0, -0.4 * options->norm->data.F32[index]); // Normalisation
    106         psBinaryOp(readout->image, readout->image, "*", psScalarAlloc(norm, PS_TYPE_F32));
    107         psBinaryOp(readout->variance, readout->variance, "*", psScalarAlloc(PS_SQR(norm), PS_TYPE_F32));
     92        // only match the flux (NO! not for multi-filter, at least!)
     93        // XXX do not generate readoutCnv in this case?
     94        // float norm = powf(10.0, -0.4 * options->norm->data.F32[index]); // Normalisation
     95        // psBinaryOp(readoutRaw->image, readoutRaw->image, "*", psScalarAlloc(norm, PS_TYPE_F32));
     96        // psBinaryOp(readoutRaw->variance, readoutRaw->variance, "*", psScalarAlloc(PS_SQR(norm), PS_TYPE_F32));
    10897    }
    10998
    110     // Ensure the background value is zero
    111     psStats *bg = psStatsAlloc(PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV); // Statistics for background
    112     psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
    113     if (!psImageBackground(bg, NULL, readout->image, readout->mask, maskVal | maskBad, rng)) {
    114         psWarning("Can't measure background for image.");
    115         psErrorClear();
    116     } else {
    117         if (!psMetadataLookupBool(NULL, config->arguments, "PPSTACK.SKIP.BG.SUB")) {
    118             psLogMsg("ppStack", PS_LOG_INFO, "Correcting convolved image background by %lf (+/- %lf)",
    119                      psStatsGetValue(bg, PS_STAT_ROBUST_MEDIAN), psStatsGetValue(bg, PS_STAT_ROBUST_STDEV));
    120             (void)psBinaryOp(readout->image, readout->image, "-",
    121                              psScalarAlloc(psStatsGetValue(bg, PS_STAT_ROBUST_MEDIAN), PS_TYPE_F32));
    122         }
    123     }
     99    rescaleData(readoutCnv, config, options, index);
    124100
    125     if (!stackRenormaliseReadout(config, readout)) {
    126         psFree(rng);
    127         psFree(bg);
    128         return false;
    129     }
    130 
    131     // Measure the variance level for the weighting
    132     if (psMetadataLookupBool(NULL, stackRecipe, "WEIGHTS")) {
    133         if (!psImageBackground(bg, NULL, readout->variance, readout->mask, maskVal | maskBad, rng)) {
    134             psError(PPSTACK_ERR_DATA, false, "Can't measure mean variance for image.");
    135             psFree(rng);
    136             psFree(bg);
    137             return false;
    138         }
    139         options->weightings->data.F32[index] = 1.0 / (psStatsGetValue(bg, PS_STAT_ROBUST_MEDIAN) * psImageCovarianceFactor(readout->covariance));
    140     } else {
    141         options->weightings->data.F32[index] = 1.0;
    142     }
    143     psLogMsg("ppStack", PS_LOG_INFO, "Weighting for image %d is %f\n",
    144              index, options->weightings->data.F32[index]);
    145 
    146     psFree(rng);
    147     psFree(bg);
    148 
    149     dumpImage3();
     101    dumpImage(readoutCnv, readoutRaw, index, "convolved");
    150102
    151103    return true;
    152104}
     105
     106
     107# if (0)
     108// Read previously produced kernel
     109if (psMetadataLookupBool(NULL, config->arguments, "PPSTACK.DEBUG.STACK")) {
     110    loadKernel(config, readoutCnv, options, index);
     111} else {
     112    matchKernel(config, readoutCnv, readoutRaw, options, index);
     113}
     114# endif
  • branches/eam_branches/psphot.20100506/src/psphotStackMatchPSFsUtils.c

    r27850 r27876  
    1 /***** defines *****/
    2 
    3 #define ARRAY_BUFFER 16                 // Number to add to array at a time
    4 #define MAG_IGNORE 50                   // Ignore magnitudes fainter than this --- they're not real!
    5 #define FAKE_SIZE 1                     // Size of fake convolution kernel
    6 #define SOURCE_MASK (PM_SOURCE_MODE_FAIL | PM_SOURCE_MODE_DEFECT | PM_SOURCE_MODE_SATURATED | PM_SOURCE_MODE_CR_LIMIT | PM_SOURCE_MODE_EXT_LIMIT) // Mask to apply to input sources
    7 #define NOISE_FRACTION 0.01             // Set minimum flux to this fraction of noise
    8 #define COVAR_FRAC 0.01                 // Truncation fraction for covariance matrix
     1# include "psphotInternal.h"
     2# define ARRAY_BUFFER 16                 // Number to add to array at a time
    93
    104// XXX better name
     
    1812    psFree(resolved);
    1913    if (!fits) {
    20         psError(PPSTACK_ERR_IO, false, "Unable to open previously produced image: %s", name);
     14        psError(PSPHOT_ERR_IO, false, "Unable to open previously produced image: %s", name);
    2115        return false;
    2216    }
    2317    psImage *image = psFitsReadImage(fits, psRegionSet(0,0,0,0), 0); // Image of interest
    2418    if (!image) {
    25         psError(PPSTACK_ERR_IO, false, "Unable to read previously produced image: %s", name);
     19        psError(PSPHOT_ERR_IO, false, "Unable to read previously produced image: %s", name);
    2620        psFitsClose(fits);
    2721        return false;
     
    5246
    5347psArray *stackSourcesFilter(psArray *sources, // Source list to filter
    54                                    int exclusion // Exclusion zone, pixels
     48                            int exclusion // Exclusion zone, pixels
    5549    )
    5650{
     
    9084
    9185        long numWithin = psTreeWithin(tree, coords, exclusion); // Number within exclusion zone
    92         psTrace("ppStack", 9, "Source at %.0lf,%.0lf has %ld sources in exclusion zone",
     86        psTrace("psphotStack", 9, "Source at %.0lf,%.0lf has %ld sources in exclusion zone",
    9387                coords->data.F64[0], coords->data.F64[1], numWithin);
    9488        if (numWithin == 1) {
     
    10498    psFree(y);
    10599
    106     psLogMsg("ppStack", PS_LOG_INFO, "Filtered out %d of %d sources", numFiltered, numGood);
     100    psLogMsg("psphotStack", PS_LOG_INFO, "Filtered out %d of %d sources", numFiltered, numGood);
    107101
    108102    return filtered;
     
    111105// Add background into the fake image
    112106// Based on ppSubBackground()
    113 static psImage *stackBackgroundModel(pmReadout *ro, // Readout for which to generate background model
     107psImage *stackBackgroundModel(pmReadout *ro, // Readout for which to generate background model
    114108                                     const pmConfig *config // Configuration
    115109    )
     
    121115    int numCols = image->numCols, numRows = image->numRows; // Size of image
    122116
    123     psMetadata *ppStackRecipe = psMetadataLookupPtr(NULL, config->recipes, PPSTACK_RECIPE);
     117    psMetadata *ppStackRecipe = psMetadataLookupPtr(NULL, config->recipes, "PPSTACK");
    124118    psAssert(ppStackRecipe, "Need PPSTACK recipe");
    125     psMetadata *psphotRecipe = psMetadataLookupPtr(NULL, config->recipes, PSPHOT_RECIPE);
     119    psMetadata *psphotRecipe = psMetadataLookupPtr(NULL, config->recipes, "PSPHOT");
    126120    psAssert(psphotRecipe, "Need PSPHOT recipe");
    127121
     
    138132    psImage *unbinned = psImageAlloc(numCols, numRows, PS_TYPE_F32); // Unbinned background model
    139133    if (!psImageUnbin(unbinned, binned, binning)) {
    140         psError(PPSTACK_ERR_DATA, false, "Unable to unbin background model");
     134        psError(PSPHOT_ERR_DATA, false, "Unable to unbin background model");
    141135        psFree(binned);
    142136        psFree(unbinned);
     
    153147    )
    154148{
    155 #if 1
    156149    bool mdok; // Status of metadata lookups
    157150
    158     psMetadata *recipe = psMetadataLookupPtr(NULL, config->recipes, PPSTACK_RECIPE); // Recipe for ppStack
     151    psMetadata *recipe = psMetadataLookupPtr(NULL, config->recipes, "PPSTACK"); // Recipe for ppStack
    159152    psAssert(recipe, "Need PPSTACK recipe");
    160153
     
    163156    int num = psMetadataLookupS32(&mdok, recipe, "RENORM.NUM");
    164157    if (!mdok) {
    165         psError(PPSTACK_ERR_CONFIG, true, "RENORM.NUM is not set in the recipe");
     158        psError(PSPHOT_ERR_CONFIG, true, "RENORM.NUM is not set in the recipe");
    166159        return false;
    167160    }
    168161    float minValid = psMetadataLookupF32(&mdok, recipe, "RENORM.MIN");
    169162    if (!mdok) {
    170         psError(PPSTACK_ERR_CONFIG, true, "RENORM.MIN is not set in the recipe");
     163        psError(PSPHOT_ERR_CONFIG, true, "RENORM.MIN is not set in the recipe");
    171164        return false;
    172165    }
    173166    float maxValid = psMetadataLookupF32(&mdok, recipe, "RENORM.MAX");
    174167    if (!mdok) {
    175         psError(PPSTACK_ERR_CONFIG, true, "RENORM.MAX is not set in the recipe");
     168        psError(PSPHOT_ERR_CONFIG, true, "RENORM.MAX is not set in the recipe");
    176169        return false;
    177170    }
     
    181174    psImageCovarianceTransfer(readout->variance, readout->covariance);
    182175    return pmReadoutVarianceRenormalise(readout, maskBad, num, minValid, maxValid);
    183 #else
    184     return true;
    185 #endif
    186176}
    187177
     
    190180// It implicitly assumes the output root name is the same between invocations.
    191181
    192 bool loadKernel () {
    193             pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPSTACK.CONV.KERNEL", index);
    194             psAssert(file, "Require file");
    195 
    196             pmFPAview *view = pmFPAviewAlloc(0); // View to readout of interest
    197             view->chip = view->cell = view->readout = 0;
    198             psString filename = pmFPAfileNameFromRule(file->filerule, file, view); // Filename of interest
    199 
    200             // Read convolution kernel
    201             psString resolved = pmConfigConvertFilename(filename, config, false, false); // Resolved filename
    202             psFree(filename);
    203             psFits *fits = psFitsOpen(resolved, "r"); // FITS file for subtraction kernel
    204             psFree(resolved);
    205             if (!fits || !pmReadoutReadSubtractionKernels(conv, fits)) {
    206                 psError(PPSTACK_ERR_IO, false, "Unable to read previously produced kernel");
    207                 psFitsClose(fits);
    208                 return false;
    209             }
    210             psFitsClose(fits);
    211 
    212             if (!readImage(&readout->image, options->convImages->data[index], config) ||
    213                 !readImage(&readout->mask, options->convMasks->data[index], config) ||
    214                 !readImage(&readout->variance, options->convVariances->data[index], config)) {
    215                 psError(PPSTACK_ERR_IO, false, "Unable to read previously produced image.");
    216                 return false;
    217             }
    218 
    219             psRegion *region = psMetadataLookupPtr(NULL, conv->analysis,
    220                                                    PM_SUBTRACTION_ANALYSIS_REGION); // Convolution region
    221             pmSubtractionKernels *kernels = psMetadataLookupPtr(NULL, conv->analysis,
    222                                                                 PM_SUBTRACTION_ANALYSIS_KERNEL);
    223 
    224             pmSubtractionAnalysis(conv->analysis, NULL, kernels, region,
    225                                   readout->image->numCols, readout->image->numRows);
    226 
    227             psKernel *kernel = pmSubtractionKernel(kernels, 0.0, 0.0, false); // Convolution kernel
    228             bool oldThreads = psImageCovarianceSetThreads(true);              // Old thread setting
    229             psKernel *covar = psImageCovarianceCalculate(kernel, readout->covariance); // Covariance matrix
    230             psImageCovarianceSetThreads(oldThreads);
    231             psFree(readout->covariance);
    232             readout->covariance = covar;
    233             psFree(kernel);
    234 }
    235 
    236 bool dumpImage() {
    237     // XXX should be optional
    238             {
    239                 pmHDU *hdu = pmHDUFromCell(readout->parent);
    240                 psString name = NULL;
    241                 psStringAppend(&name, "fake_%03d.fits", index);
    242                 pmStackVisualPlotTestImage(fake->image, name);
    243                 psFits *fits = psFitsOpen(name, "w");
    244                 psFree(name);
    245                 psFitsWriteImage(fits, hdu->header, fake->image, 0, NULL);
    246                 psFitsClose(fits);
    247             }
    248             {
    249                 pmHDU *hdu = pmHDUFromCell(readout->parent);
    250                 psString name = NULL;
    251                 psStringAppend(&name, "real_%03d.fits", index);
    252                 pmStackVisualPlotTestImage(readout->image, name);
    253                 psFits *fits = psFitsOpen(name, "w");
    254                 psFree(name);
    255                 psFitsWriteImage(fits, hdu->header, readout->image, 0, NULL);
    256                 psFitsClose(fits);
    257             }
    258 }
    259 
    260 bool dumpImage2() {
    261     // XXX should be optional
    262 
    263             {
    264                 pmHDU *hdu = pmHDUFromCell(readout->parent);
    265                 psString name = NULL;
    266                 psStringAppend(&name, "conv_%03d.fits", index);
    267                 pmStackVisualPlotTestImage(conv->image, name);
    268                 psFits *fits = psFitsOpen(name, "w");
    269                 psFree(name);
    270                 psFitsWriteImage(fits, hdu->header, conv->image, 0, NULL);
    271                 psFitsClose(fits);
    272             }
    273             {
    274                 pmHDU *hdu = pmHDUFromCell(readout->parent);
    275                 psString name = NULL;
    276                 psStringAppend(&name, "diff_%03d.fits", index);
    277                 pmStackVisualPlotTestImage(fake->image, name);
    278                 psFits *fits = psFitsOpen(name, "w");
    279                 psFree(name);
    280                 psBinaryOp(fake->image, conv->image, "-", fake->image);
    281                 psFitsWriteImage(fits, hdu->header, fake->image, 0, NULL);
    282                 psFitsClose(fits);
    283             }
    284 }
    285 
    286 bool dumpImage3()
     182bool loadKernel (pmConfig *config, pmReadout *readoutCnv, psphotStackOptions *options, int index) {
     183
     184    // Read the convolution kernel from the saved file
     185    pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPSTACK.CONV.KERNEL", index);
     186    psAssert(file, "Require file");
     187
     188    pmFPAview *view = pmFPAviewAlloc(0); // View to readout of interest
     189    view->chip = view->cell = view->readout = 0;
     190    psString filename = pmFPAfileNameFromRule(file->filerule, file, view); // Filename of interest
     191
     192    // Read convolution kernel data
     193    psString resolved = pmConfigConvertFilename(filename, config, false, false); // Resolved filename
     194    psFree(filename);
     195    psFits *fits = psFitsOpen(resolved, "r"); // FITS file for subtraction kernel
     196    psFree(resolved);
     197    if (!fits || !pmReadoutReadSubtractionKernels(readoutCnv, fits)) {
     198        psError(PSPHOT_ERR_IO, false, "Unable to read previously produced kernel");
     199        psFitsClose(fits);
     200        return false;
     201    }
     202    psFitsClose(fits);
     203
     204    // read the convolved pixels (image, mask, variance) -- names are pre-defined
     205    if (!readImage(&readoutCnv->image,    options->convImages->data[index],    config) ||
     206        !readImage(&readoutCnv->mask,     options->convMasks->data[index],     config) ||
     207        !readImage(&readoutCnv->variance, options->convVariances->data[index], config)) {
     208        psError(PSPHOT_ERR_IO, false, "Unable to read previously produced image.");
     209        return false;
     210    }
     211
     212    // XXX ??? not sure what is happening here -- consult Paul
     213    psRegion *region = psMetadataLookupPtr(NULL, readoutCnv->analysis, PM_SUBTRACTION_ANALYSIS_REGION); // Convolution region
     214    pmSubtractionKernels *kernels = psMetadataLookupPtr(NULL, readoutCnv->analysis, PM_SUBTRACTION_ANALYSIS_KERNEL);
     215
     216    pmSubtractionAnalysis(readoutCnv->analysis, NULL, kernels, region, readoutCnv->image->numCols, readoutCnv->image->numRows);
     217
     218    psKernel *kernel = pmSubtractionKernel(kernels, 0.0, 0.0, false); // Convolution kernel
     219
     220    // update the covariance matrix
     221    // XXX why is this needed if we have correctly read the saved data?
     222    bool oldThreads = psImageCovarianceSetThreads(true);              // Old thread setting
     223    psKernel *covar = psImageCovarianceCalculate(kernel, readoutCnv->covariance); // Covariance matrix
     224    psImageCovarianceSetThreads(oldThreads);
     225    psFree(readoutCnv->covariance);
     226    readoutCnv->covariance = covar;
     227    psFree(kernel);
     228    return true;
     229}
     230
     231bool dumpImage(pmReadout *readoutOut, pmReadout *readoutRef, int index, char *rootname) {
     232
     233    pmHDU *hdu = pmHDUFromCell(readoutRef->parent);
     234    psString name = NULL;
     235    psStringAppend(&name, "%s_%03d.fits", rootname, index);
     236    pmStackVisualPlotTestImage(readoutOut->image, name);
     237    psFits *fits = psFitsOpen(name, "w");
     238    psFree(name);
     239    psFitsWriteImage(fits, hdu->header, readoutOut->image, 0, NULL);
     240    psFitsClose(fits);
     241    return true;
     242}
     243
     244bool dumpImageDiff(pmReadout *readoutConv, pmReadout *readoutFake, pmReadout *readoutRef, int index, char *rootname) {
     245
     246    pmHDU *hdu = pmHDUFromCell(readoutRef->parent);
     247    psString name = NULL;
     248    psStringAppend(&name, "%s_%03d.fits", rootname, index);
     249    pmStackVisualPlotTestImage(readoutFake->image, name);
     250    psFits *fits = psFitsOpen(name, "w");
     251    psFree(name);
     252    psBinaryOp(readoutFake->image, readoutConv->image, "-", readoutFake->image);
     253    psFitsWriteImage(fits, hdu->header, readoutFake->image, 0, NULL);
     254    psFitsClose(fits);
     255    return true;
     256}
     257
     258// perform the bulk of the PSF-matching
     259bool matchKernel(pmConfig *config, pmReadout *readoutCnv, pmReadout *readoutRaw, psphotStackOptions *options, int index) {
     260
     261    bool mdok;
     262
     263    psAssert(options->psf, "Require target PSF");
     264    psAssert(options->sourceLists && options->sourceLists->data[index], "Require source list");
     265
     266    psMetadata *stackRecipe = psMetadataLookupMetadata(NULL, config->recipes, "PPSTACK"); // ppStack recipe
     267    psAssert(stackRecipe, "We've thrown an error on this before.");
     268
     269    // Look up appropriate values from the ppSub recipe
     270    psMetadata *subRecipe = psMetadataLookupMetadata(NULL, config->recipes, "PPSUB"); // PPSUB recipe
     271    psAssert(subRecipe, "recipe missing");
     272
     273    psString maskValStr = psMetadataLookupStr(NULL, subRecipe, "MASK.VAL"); // Name of bits to mask going in
     274    psString maskPoorStr = psMetadataLookupStr(NULL, stackRecipe, "MASK.POOR"); // Name of bits to mask for poor
     275    psString maskBadStr = psMetadataLookupStr(NULL, stackRecipe, "MASK.BAD"); // Name of bits to mask for bad
     276
     277    psImageMaskType maskVal = pmConfigMaskGet(maskValStr, config); // Bits to mask going in to pmSubtractionMatch
     278    psImageMaskType maskPoor = pmConfigMaskGet(maskPoorStr, config); // Bits to mask for poor pixels
     279    psImageMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
     280
     281    float penalty = psMetadataLookupF32(NULL, subRecipe, "PENALTY"); // Penalty for wideness
     282    int threads = psMetadataLookupS32(NULL, config->arguments, "-threads"); // Number of threads
     283
     284    int order = psMetadataLookupS32(NULL, subRecipe, "SPATIAL.ORDER"); // Spatial polynomial order
     285    float regionSize = psMetadataLookupF32(NULL, subRecipe, "REGION.SIZE"); // Size of iso-kernel regs
     286    float spacing = psMetadataLookupF32(NULL, subRecipe, "STAMP.SPACING"); // Typical stamp spacing
     287
     288    int footprint = psMetadataLookupS32(NULL, subRecipe, "STAMP.FOOTPRINT"); // Stamp half-size
     289    int size = psMetadataLookupS32(NULL, subRecipe, "KERNEL.SIZE"); // Kernel half-size
     290
     291    float threshold = psMetadataLookupF32(NULL, subRecipe, "STAMP.THRESHOLD"); // Threshold for stmps
     292    int stride = psMetadataLookupS32(NULL, subRecipe, "STRIDE"); // Size of convolution patches
     293    int iter = psMetadataLookupS32(NULL, subRecipe, "ITER"); // Rejection iterations
     294    float rej = psMetadataLookupF32(NULL, subRecipe, "REJ"); // Rejection threshold
     295    float kernelError = psMetadataLookupF32(NULL, subRecipe, "KERNEL.ERR"); // Relative systematic error in kernel
     296    float normFrac = psMetadataLookupF32(NULL, subRecipe, "NORM.FRAC"); // Fraction of window for normalisn windw
     297    float sysError = psMetadataLookupF32(NULL, subRecipe, "SYS.ERR"); // Relative systematic error in images
     298    float skyErr = psMetadataLookupF32(NULL, subRecipe, "SKY.ERR"); // Additional error in sky
     299    float covarFrac = psMetadataLookupF32(NULL, subRecipe, "COVAR.FRAC"); // Fraction for covariance calculation
     300
     301    const char *typeStr = psMetadataLookupStr(NULL, subRecipe, "KERNEL.TYPE"); // Kernel type
     302    pmSubtractionKernelsType type = pmSubtractionKernelsTypeFromString(typeStr); // Kernel type
     303    psVector *widths = psMetadataLookupPtr(NULL, subRecipe, "ISIS.WIDTHS"); // ISIS Gaussian widths
     304    psVector *orders = psMetadataLookupPtr(NULL, subRecipe, "ISIS.ORDERS"); // ISIS Polynomial orders
     305    int inner = psMetadataLookupS32(NULL, subRecipe, "INNER"); // Inner radius
     306    int ringsOrder = psMetadataLookupS32(NULL, subRecipe, "RINGS.ORDER"); // RINGS polynomial order
     307    int binning = psMetadataLookupS32(NULL, subRecipe, "SPAM.BINNING"); // Binning for SPAM kernel
     308    float badFrac = psMetadataLookupF32(NULL, subRecipe, "BADFRAC"); // Maximum bad fraction
     309    float optThresh = psMetadataLookupF32(&mdok, subRecipe, "OPTIMUM.TOL"); // Tolerance for search
     310    int optOrder = psMetadataLookupS32(&mdok, subRecipe, "OPTIMUM.ORDER"); // Order for search
     311    float poorFrac = psMetadataLookupF32(&mdok, subRecipe, "POOR.FRACTION"); // Fraction for "poor"
     312
     313    bool scale = psMetadataLookupBool(NULL, subRecipe, "SCALE");        // Scale kernel parameters?
     314    float scaleRef = psMetadataLookupF32(NULL, subRecipe, "SCALE.REF"); // Reference for scaling
     315    float scaleMin = psMetadataLookupF32(NULL, subRecipe, "SCALE.MIN"); // Minimum for scaling
     316    float scaleMax = psMetadataLookupF32(NULL, subRecipe, "SCALE.MAX"); // Maximum for scaling
     317    if (!isfinite(scaleRef) || !isfinite(scaleMin) || !isfinite(scaleMax)) {
     318        psError(PSPHOT_ERR_CONFIG, false,
     319                "Scale parameters (SCALE.REF=%f, SCALE.MIN=%f, SCALE.MAX=%f) not set in PPSUB recipe.",
     320                scaleRef, scaleMin, scaleMax);
     321        return false;
     322    }
     323
     324    // These values are specified specifically for stacking
     325    const char *stampsName = psMetadataLookupStr(NULL, config->arguments, "STAMPS");// Stamps filename
     326
     327    psVector *widthsCopy = NULL;
     328    psVector *optWidths = NULL;
     329    pmReadout *fake = NULL;
     330    psArray *stampSources = NULL;
     331
     332    bool optimum = false;
     333    optWidths = SetOptWidths(&optimum, subRecipe); // Vector with FWHMs for optimum search
     334
     335    // For the sake of stamps, remove nearby sources
     336    stampSources = stackSourcesFilter(options->sourceLists->data[index], footprint); // Filtered list of sources
     337
     338    fake = makeFakeReadout(config, readoutRaw, stampSources, options->psf, maskVal | maskBad, footprint + size);
     339    if (!fake) goto escape;
     340
     341    dumpImage(fake, readoutRaw, index, "fake");
     342    dumpImage(readoutRaw,  readoutRaw, index, "real");
     343
     344    if (threads) pmSubtractionThreadsInit();
     345
     346    // Do the image matching
     347    pmSubtractionKernels *kernel = psMetadataLookupPtr(&mdok, readoutRaw->analysis, PM_SUBTRACTION_ANALYSIS_KERNEL); // Conv kernel
     348    if (kernel) {
     349        if (!pmSubtractionMatchPrecalc(NULL, readoutCnv, fake, readoutRaw, readoutRaw->analysis, stride, kernelError, covarFrac, maskVal, maskBad, maskPoor, poorFrac, badFrac)) {
     350            psError(psErrorCodeLast(), false, "Unable to convolve images.");
     351            goto escape;
     352        }
     353    } else {
     354        // Scale the input parameters
     355        widthsCopy = psVectorCopy(NULL, widths, PS_TYPE_F32); // Copy of kernel widths
     356        if (scale && !pmSubtractionParamsScale(&size, &footprint, widthsCopy, options->inputSeeing->data.F32[index], options->targetSeeing, scaleRef, scaleMin, scaleMax)) {
     357            psError(psErrorCodeLast(), false, "Unable to scale kernel parameters");
     358            goto escape;
     359        }
     360
     361        if (!pmSubtractionMatch(NULL, readoutCnv, fake, readoutRaw, footprint, stride, regionSize, spacing, threshold, stampSources, stampsName, type, size, order, widthsCopy, orders, inner, ringsOrder, binning, penalty, optimum, optWidths, optOrder, optThresh, iter, rej, normFrac, sysError, skyErr, kernelError, covarFrac, maskVal, maskBad, maskPoor, poorFrac, badFrac, PM_SUBTRACTION_MODE_2)) {
     362            psError(psErrorCodeLast(), false, "Unable to match images.");
     363            goto escape;
     364        }
     365    }
     366
     367    // Reject image completely if the maximum deconvolution fraction exceeds the limit
     368    float deconvLimit = psMetadataLookupF32(NULL, stackRecipe, "DECONV.LIMIT"); // Limit on deconvolution fraction
     369    float deconv = psMetadataLookupF32(NULL, readoutCnv->analysis, PM_SUBTRACTION_ANALYSIS_DECONV_MAX); // Max deconvolution fraction
     370    if (deconv > deconvLimit) {
     371        psWarning("Maximum deconvolution fraction (%f) exceeds limit (%f) --- rejecting image %d\n", deconv, deconvLimit, index);
     372        goto escape;
     373    }
     374
     375    dumpImage(readoutCnv, readoutRaw, index, "conv");
     376    dumpImageDiff(readoutCnv, fake, readoutRaw, index, "diff");
     377
     378    psFree(fake);
     379    psFree(optWidths);
     380    psFree(stampSources);
     381    psFree(widthsCopy);
     382    pmSubtractionThreadsFinalize();
     383    return true;
     384
     385escape:
     386    psFree(fake);
     387    psFree(optWidths);
     388    psFree(stampSources);
     389    psFree(widthsCopy);
     390    pmSubtractionThreadsFinalize();
     391    return false;
     392}
     393
     394// Extract the regions and solutions used in the image matching
     395// This stops them from being freed when we iterate back up the FPA
     396// Record the chi-square value
     397// XXX this function may not be needed for psphotStack
     398bool saveMatchData (pmReadout *readout, psphotStackOptions *options, int index) {
     399
     400    psArray *regions = options->regions->data[index] = psArrayAllocEmpty(ARRAY_BUFFER); // Match regions
    287401    {
    288         pmHDU *hdu = pmHDUFromCell(readout->parent);
    289         psString name = NULL;
    290         psStringAppend(&name, "convolved_%03d.fits", index);
    291         pmStackVisualPlotTestImage(readout->image, name);
    292         psFits *fits = psFitsOpen(name, "w");
    293         psFree(name);
    294         psFitsWriteImage(fits, hdu->header, readout->image, 0, NULL);
    295         psFitsClose(fits);
    296     }
    297 
    298 bool matchKernel() {
    299             // Normal operations here
    300             psAssert(options->psf, "Require target PSF");
    301             psAssert(options->sourceLists && options->sourceLists->data[index], "Require source list");
    302 
    303             int order = psMetadataLookupS32(NULL, subRecipe, "SPATIAL.ORDER"); // Spatial polynomial order
    304             float regionSize = psMetadataLookupF32(NULL, subRecipe, "REGION.SIZE"); // Size of iso-kernel regs
    305             float spacing = psMetadataLookupF32(NULL, subRecipe, "STAMP.SPACING"); // Typical stamp spacing
    306             int footprint = psMetadataLookupS32(NULL, subRecipe, "STAMP.FOOTPRINT"); // Stamp half-size
    307             float threshold = psMetadataLookupF32(NULL, subRecipe, "STAMP.THRESHOLD"); // Threshold for stmps
    308             int stride = psMetadataLookupS32(NULL, subRecipe, "STRIDE"); // Size of convolution patches
    309             int iter = psMetadataLookupS32(NULL, subRecipe, "ITER"); // Rejection iterations
    310             float rej = psMetadataLookupF32(NULL, subRecipe, "REJ"); // Rejection threshold
    311             float kernelError = psMetadataLookupF32(NULL, subRecipe, "KERNEL.ERR"); // Relative systematic error in kernel
    312             float normFrac = psMetadataLookupF32(NULL, subRecipe, "NORM.FRAC"); // Fraction of window for normalisn windw
    313             float sysError = psMetadataLookupF32(NULL, subRecipe, "SYS.ERR"); // Relative systematic error in images
    314             float skyErr = psMetadataLookupF32(NULL, subRecipe, "SKY.ERR"); // Additional error in sky
    315             float covarFrac = psMetadataLookupF32(NULL, subRecipe, "COVAR.FRAC"); // Fraction for covariance calculation
    316 
    317             const char *typeStr = psMetadataLookupStr(NULL, subRecipe, "KERNEL.TYPE"); // Kernel type
    318             pmSubtractionKernelsType type = pmSubtractionKernelsTypeFromString(typeStr); // Kernel type
    319             psVector *widths = psMetadataLookupPtr(NULL, subRecipe, "ISIS.WIDTHS"); // ISIS Gaussian widths
    320             psVector *orders = psMetadataLookupPtr(NULL, subRecipe, "ISIS.ORDERS"); // ISIS Polynomial orders
    321             int inner = psMetadataLookupS32(NULL, subRecipe, "INNER"); // Inner radius
    322             int ringsOrder = psMetadataLookupS32(NULL, subRecipe, "RINGS.ORDER"); // RINGS polynomial order
    323             int binning = psMetadataLookupS32(NULL, subRecipe, "SPAM.BINNING"); // Binning for SPAM kernel
    324             float badFrac = psMetadataLookupF32(NULL, subRecipe, "BADFRAC"); // Maximum bad fraction
    325             bool optimum = psMetadataLookupBool(&mdok, subRecipe, "OPTIMUM"); // Derive optimum parameters?
    326             float optMin = psMetadataLookupF32(&mdok, subRecipe, "OPTIMUM.MIN"); // Minimum width for search
    327             float optMax = psMetadataLookupF32(&mdok, subRecipe, "OPTIMUM.MAX"); // Maximum width for search
    328             float optStep = psMetadataLookupF32(&mdok, subRecipe, "OPTIMUM.STEP"); // Step for search
    329             float optThresh = psMetadataLookupF32(&mdok, subRecipe, "OPTIMUM.TOL"); // Tolerance for search
    330             int optOrder = psMetadataLookupS32(&mdok, subRecipe, "OPTIMUM.ORDER"); // Order for search
    331             float poorFrac = psMetadataLookupF32(&mdok, subRecipe, "POOR.FRACTION"); // Fraction for "poor"
    332 
    333             bool scale = psMetadataLookupBool(NULL, subRecipe, "SCALE");        // Scale kernel parameters?
    334             float scaleRef = psMetadataLookupF32(NULL, subRecipe, "SCALE.REF"); // Reference for scaling
    335             float scaleMin = psMetadataLookupF32(NULL, subRecipe, "SCALE.MIN"); // Minimum for scaling
    336             float scaleMax = psMetadataLookupF32(NULL, subRecipe, "SCALE.MAX"); // Maximum for scaling
    337             if (!isfinite(scaleRef) || !isfinite(scaleMin) || !isfinite(scaleMax)) {
    338                 psError(PPSTACK_ERR_CONFIG, false,
    339                         "Scale parameters (SCALE.REF=%f, SCALE.MIN=%f, SCALE.MAX=%f) not set in PPSUB recipe.",
    340                         scaleRef, scaleMin, scaleMax);
    341                 return false;
    342             }
    343 
    344 
    345             // These values are specified specifically for stacking
    346             const char *stampsName = psMetadataLookupStr(NULL, config->arguments, "STAMPS");// Stamps filename
    347 
    348             psVector *optWidths = NULL;         // Vector with FWHMs for optimum search
    349             if (optimum) {
    350                 optWidths = psVectorCreate(optWidths, optMin, optMax, optStep, PS_TYPE_F32);
    351             }
    352 
    353             pmReadout *fake = pmReadoutAlloc(NULL); // Fake readout with target PSF
    354 
    355             psStats *bg = psStatsAlloc(PS_STAT_ROBUST_STDEV); // Statistics for background
    356             psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
    357             if (!psImageBackground(bg, NULL, readout->image, readout->mask, maskVal | maskBad, rng)) {
    358                 psError(PPSTACK_ERR_DATA, false, "Can't measure background for image.");
    359                 psFree(fake);
    360                 psFree(optWidths);
    361                 psFree(conv);
    362                 psFree(bg);
    363                 psFree(rng);
    364                 return false;
    365             }
    366             float minFlux = NOISE_FRACTION * bg->robustStdev; // Minimum flux level for fake image
     402        psString regex = NULL;          // Regular expression
     403        psStringAppend(&regex, "^%s$", PM_SUBTRACTION_ANALYSIS_REGION);
     404        psMetadataIterator *iter = psMetadataIteratorAlloc(readout->analysis, PS_LIST_HEAD, regex);
     405        psFree(regex);
     406        psMetadataItem *item = NULL;// Item from iteration
     407        while ((item = psMetadataGetAndIncrement(iter))) {
     408            assert(item->type == PS_DATA_REGION);
     409            regions = psArrayAdd(regions, ARRAY_BUFFER, item->data.V);
     410        }
     411        psFree(iter);
     412    }
     413
     414    psArray *kernels = options->kernels->data[index] = psArrayAllocEmpty(ARRAY_BUFFER); // Match kernels
     415    {
     416        psString regex = NULL;          // Regular expression
     417        psStringAppend(&regex, "^%s$", PM_SUBTRACTION_ANALYSIS_KERNEL);
     418        psMetadataIterator *iter = psMetadataIteratorAlloc(readout->analysis, PS_LIST_HEAD, regex);
     419        psFree(regex);
     420        psMetadataItem *item = NULL;// Item from iteration
     421        while ((item = psMetadataGetAndIncrement(iter))) {
     422            assert(item->type == PS_DATA_UNKNOWN);
     423            pmSubtractionKernels *kernel = item->data.V; // Kernel used in subtraction
     424            kernels = psArrayAdd(kernels, ARRAY_BUFFER, kernel);
     425        }
     426        psFree(iter);
     427    }
     428    psAssert((regions)->n == (kernels)->n, "Number of match regions and kernels should match");
     429
     430    // Record chi^2
     431    {
     432        double sum = 0.0;           // Sum of chi^2
     433        int num = 0;                // Number of measurements of chi^2
     434        psString regex = NULL;      // Regular expression
     435        psStringAppend(&regex, "^%s$", PM_SUBTRACTION_ANALYSIS_KERNEL);
     436        psMetadataIterator *iter = psMetadataIteratorAlloc(readout->analysis, PS_LIST_HEAD, regex);
     437        psFree(regex);
     438        psMetadataItem *item = NULL;// Item from iteration
     439        while ((item = psMetadataGetAndIncrement(iter))) {
     440            assert(item->type == PS_DATA_UNKNOWN);
     441            pmSubtractionKernels *kernels = item->data.V; // Convolution kernels
     442            sum += kernels->mean;
     443            num++;
     444        }
     445        psFree(iter);
     446        options->matchChi2->data.F32[index] = sum / (psImageCovarianceFactor(readout->covariance) * num);
     447    }
     448
     449    return true;
     450}
     451
     452// Kernel normalisation for convolved readout
     453bool renormKernel(pmReadout *readout, psphotStackOptions *options, int index) {
     454
     455    double sum = 0.0;           // Sum of chi^2
     456    int num = 0;                // Number of measurements of chi^2
     457    psString regex = NULL;      // Regular expression
     458    psStringAppend(&regex, "^%s$", PM_SUBTRACTION_ANALYSIS_NORM);
     459    psMetadataIterator *iter = psMetadataIteratorAlloc(readout->analysis, PS_LIST_HEAD, regex);
     460    psFree(regex);
     461    psMetadataItem *item = NULL;// Item from iteration
     462    while ((item = psMetadataGetAndIncrement(iter))) {
     463        assert(item->type == PS_TYPE_F32);
     464        float norm = item->data.F32; // Normalisation
     465        sum += norm;
     466        num++;
     467    }
     468    psFree(iter);
     469    float conv = sum/num;       // Mean normalisation from convolution
     470    float stars = powf(10.0, -0.4 * options->norm->data.F32[index]); // Normalisation from stars
     471    float renorm =  stars / conv; // Renormalisation to apply
     472    psLogMsg("psphotStack", PS_LOG_INFO, "Renormalising image %d by %f (kernel: %f, stars: %f)\n", index, renorm, conv, stars);
     473
     474    psBinaryOp(readout->image, readout->image, "*", psScalarAlloc(renorm, PS_TYPE_F32));
     475    psBinaryOp(readout->variance, readout->variance, "*", psScalarAlloc(PS_SQR(renorm), PS_TYPE_F32));
     476    return true;
     477}
     478
     479// adjust scaling for readout (remove background, ..., determine weighting)
     480bool rescaleData(pmReadout *readout, pmConfig *config, psphotStackOptions *options, int index) {
     481
     482    psMetadata *stackRecipe = psMetadataLookupMetadata(NULL, config->recipes, "PPSTACK"); // ppStack recipe
     483    psAssert(stackRecipe, "We've thrown an error on this before.");
     484
     485    // Look up appropriate values from the ppSub recipe
     486    psMetadata *subRecipe = psMetadataLookupMetadata(NULL, config->recipes, "PPSUB"); // PPSUB recipe
     487    psAssert(subRecipe, "recipe missing");
     488
     489    psString maskValStr = psMetadataLookupStr(NULL, subRecipe, "MASK.VAL"); // Name of bits to mask going in
     490    psString maskBadStr = psMetadataLookupStr(NULL, stackRecipe, "MASK.BAD"); // Name of bits to mask for bad
     491
     492    psImageMaskType maskVal = pmConfigMaskGet(maskValStr, config); // Bits to mask going in to pmSubtractionMatch
     493    psImageMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
     494
     495    // Ensure the background value is zero
     496    psStats *bg = psStatsAlloc(PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV); // Statistics for background
     497    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
     498
     499    // XXX why is this in config->arguments and not recipe?
     500    if (!psMetadataLookupBool(NULL, config->arguments, "PPSTACK.SKIP.BG.SUB")) {
     501        if (!psImageBackground(bg, NULL, readout->image, readout->mask, maskVal | maskBad, rng)) {
     502            psAbort("Can't measure background for image.");
     503            // XXX we used to clear error: why is this acceptable? psErrorClear();
     504        }
     505
     506        float value = psStatsGetValue(bg, PS_STAT_ROBUST_MEDIAN);
     507        float stdev = psStatsGetValue(bg, PS_STAT_ROBUST_STDEV);
     508
     509        psLogMsg("psphotStack", PS_LOG_INFO, "Correcting convolved image background by %lf (+/- %lf)", value, stdev);
     510        psBinaryOp(readout->image, readout->image, "-", psScalarAlloc(value, PS_TYPE_F32));
     511    }
     512
     513    if (!stackRenormaliseReadout(config, readout)) {
     514        psFree(rng);
     515        psFree(bg);
     516        return false;
     517    }
     518
     519    // Measure the variance level for the weighting
     520    if (psMetadataLookupBool(NULL, stackRecipe, "WEIGHTS")) {
     521        if (!psImageBackground(bg, NULL, readout->variance, readout->mask, maskVal | maskBad, rng)) {
     522            psError(PSPHOT_ERR_DATA, false, "Can't measure mean variance for image.");
    367523            psFree(rng);
    368524            psFree(bg);
    369 
    370             // For the sake of stamps, remove nearby sources
    371             psArray *stampSources = stackSourcesFilter(options->sourceLists->data[index],
    372                                                        footprint); // Filtered list of sources
    373 
    374             bool oldThreads = pmReadoutFakeThreads(true); // Old threading state
    375             if (!pmReadoutFakeFromSources(fake, readout->image->numCols, readout->image->numRows,
    376                                           stampSources, SOURCE_MASK, NULL, NULL, options->psf,
    377                                           minFlux, footprint + size, false, true)) {
    378                 psError(PPSTACK_ERR_DATA, false, "Unable to generate fake image with target PSF.");
    379                 psFree(fake);
    380                 psFree(optWidths);
    381                 psFree(conv);
    382                 return false;
    383             }
    384             pmReadoutFakeThreads(oldThreads);
    385 
    386             fake->mask = psImageCopy(NULL, readout->mask, PS_TYPE_IMAGE_MASK);
    387 
    388             // Add the background into the target image
    389             psImage *bgImage = stackBackgroundModel(readout, config); // Image of background
    390             psBinaryOp(fake->image, fake->image, "+", bgImage);
    391             psFree(bgImage);
    392 
    393             dumpImage();
    394 
    395             if (threads > 0) {
    396                 pmSubtractionThreadsInit();
    397             }
    398 
    399             // Do the image matching
    400             pmSubtractionKernels *kernel = psMetadataLookupPtr(&mdok, readout->analysis, PM_SUBTRACTION_ANALYSIS_KERNEL); // Conv kernel
    401             if (kernel) {
    402                 if (!pmSubtractionMatchPrecalc(NULL, conv, fake, readout, readout->analysis,
    403                                                stride, kernelError, covarFrac, maskVal, maskBad, maskPoor,
    404                                                poorFrac, badFrac)) {
    405                     psError(psErrorCodeLast(), false, "Unable to convolve images.");
    406                     psFree(fake);
    407                     psFree(optWidths);
    408                     psFree(stampSources);
    409                     psFree(conv);
    410                     if (threads > 0) {
    411                         pmSubtractionThreadsFinalize();
    412                     }
    413                     return false;
    414                 }
    415             } else {
    416                 // Scale the input parameters
    417                 psVector *widthsCopy = psVectorCopy(NULL, widths, PS_TYPE_F32); // Copy of kernel widths
    418                 if (scale && !pmSubtractionParamsScale(&size, &footprint, widthsCopy,
    419                                                        options->inputSeeing->data.F32[index],
    420                                                        options->targetSeeing, scaleRef, scaleMin, scaleMax)) {
    421                     psError(psErrorCodeLast(), false, "Unable to scale kernel parameters");
    422                     psFree(fake);
    423                     psFree(optWidths);
    424                     psFree(stampSources);
    425                     psFree(conv);
    426                     psFree(widthsCopy);
    427                     if (threads > 0) {
    428                         pmSubtractionThreadsFinalize();
    429                     }
    430                     return false;
    431                 }
    432 
    433                 if (!pmSubtractionMatch(NULL, conv, fake, readout, footprint, stride, regionSize, spacing,
    434                                         threshold, stampSources, stampsName, type, size, order, widthsCopy,
    435                                         orders, inner, ringsOrder, binning, penalty,
    436                                         optimum, optWidths, optOrder, optThresh, iter, rej, normFrac,
    437                                         sysError, skyErr, kernelError, covarFrac, maskVal, maskBad, maskPoor,
    438                                         poorFrac, badFrac, PM_SUBTRACTION_MODE_2)) {
    439                     psError(psErrorCodeLast(), false, "Unable to match images.");
    440                     psFree(fake);
    441                     psFree(optWidths);
    442                     psFree(stampSources);
    443                     psFree(conv);
    444                     psFree(widthsCopy);
    445                     if (threads > 0) {
    446                         pmSubtractionThreadsFinalize();
    447                     }
    448                     return false;
    449                 }
    450                 psFree(widthsCopy);
    451             }
    452 
    453             dumpImage2();
    454 
    455             psFree(fake);
    456             psFree(optWidths);
    457             psFree(stampSources);
    458 
    459             if (threads > 0) {
    460                 pmSubtractionThreadsFinalize();
    461             }
    462 
    463             // Replace original images with convolved
    464             psFree(readout->image);
    465             psFree(readout->mask);
    466             psFree(readout->variance);
    467             psFree(readout->covariance);
    468             readout->image  = psMemIncrRefCounter(conv->image);
    469             readout->mask   = psMemIncrRefCounter(conv->mask);
    470             readout->variance = psMemIncrRefCounter(conv->variance);
    471             readout->covariance = psImageCovarianceTruncate(conv->covariance, COVAR_FRAC);
    472 
    473 }
    474 
    475 bool saveMatchData () {
    476        // Extract the regions and solutions used in the image matching
    477         // This stops them from being freed when we iterate back up the FPA
    478         psArray *regions = options->regions->data[index] = psArrayAllocEmpty(ARRAY_BUFFER); // Match regions
    479         {
    480             psString regex = NULL;          // Regular expression
    481             psStringAppend(&regex, "^%s$", PM_SUBTRACTION_ANALYSIS_REGION);
    482             psMetadataIterator *iter = psMetadataIteratorAlloc(conv->analysis, PS_LIST_HEAD, regex);
    483             psFree(regex);
    484             psMetadataItem *item = NULL;// Item from iteration
    485             while ((item = psMetadataGetAndIncrement(iter))) {
    486                 assert(item->type == PS_DATA_REGION);
    487                 regions = psArrayAdd(regions, ARRAY_BUFFER, item->data.V);
    488             }
    489             psFree(iter);
     525            return false;
    490526        }
    491         psArray *kernels = options->kernels->data[index] = psArrayAllocEmpty(ARRAY_BUFFER); // Match kernels
    492         {
    493             psString regex = NULL;          // Regular expression
    494             psStringAppend(&regex, "^%s$", PM_SUBTRACTION_ANALYSIS_KERNEL);
    495             psMetadataIterator *iter = psMetadataIteratorAlloc(conv->analysis, PS_LIST_HEAD, regex);
    496             psFree(regex);
    497             psMetadataItem *item = NULL;// Item from iteration
    498             while ((item = psMetadataGetAndIncrement(iter))) {
    499                 assert(item->type == PS_DATA_UNKNOWN);
    500                 pmSubtractionKernels *kernel = item->data.V; // Kernel used in subtraction
    501                 kernels = psArrayAdd(kernels, ARRAY_BUFFER, kernel);
    502             }
    503             psFree(iter);
    504         }
    505         psAssert((regions)->n == (kernels)->n, "Number of match regions and kernels should match");
    506 }
    507 
    508 bool saveChiSquare() {
    509         // Record chi^2
    510         {
    511             double sum = 0.0;           // Sum of chi^2
    512             int num = 0;                // Number of measurements of chi^2
    513             psString regex = NULL;      // Regular expression
    514             psStringAppend(&regex, "^%s$", PM_SUBTRACTION_ANALYSIS_KERNEL);
    515             psMetadataIterator *iter = psMetadataIteratorAlloc(conv->analysis, PS_LIST_HEAD, regex);
    516             psFree(regex);
    517             psMetadataItem *item = NULL;// Item from iteration
    518             while ((item = psMetadataGetAndIncrement(iter))) {
    519                 assert(item->type == PS_DATA_UNKNOWN);
    520                 pmSubtractionKernels *kernels = item->data.V; // Convolution kernels
    521                 sum += kernels->mean;
    522                 num++;
    523             }
    524             psFree(iter);
    525             options->matchChi2->data.F32[index] = sum / (psImageCovarianceFactor(readout->covariance) * num);
    526         }
    527 
    528 }
    529 
    530 bool renormKernel() {
    531         // Kernel normalisation
    532         {
    533             double sum = 0.0;           // Sum of chi^2
    534             int num = 0;                // Number of measurements of chi^2
    535             psString regex = NULL;      // Regular expression
    536             psStringAppend(&regex, "^%s$", PM_SUBTRACTION_ANALYSIS_NORM);
    537             psMetadataIterator *iter = psMetadataIteratorAlloc(conv->analysis, PS_LIST_HEAD, regex);
    538             psFree(regex);
    539             psMetadataItem *item = NULL;// Item from iteration
    540             while ((item = psMetadataGetAndIncrement(iter))) {
    541                 assert(item->type == PS_TYPE_F32);
    542                 float norm = item->data.F32; // Normalisation
    543                 sum += norm;
    544                 num++;
    545             }
    546             psFree(iter);
    547             float conv = sum/num;       // Mean normalisation from convolution
    548             float stars = powf(10.0, -0.4 * options->norm->data.F32[index]); // Normalisation from stars
    549             float renorm =  stars / conv; // Renormalisation to apply
    550             psLogMsg("ppStack", PS_LOG_INFO, "Renormalising image %d by %f (kernel: %f, stars: %f)\n",
    551                      index, renorm, conv, stars);
    552             psBinaryOp(readout->image, readout->image, "*", psScalarAlloc(renorm, PS_TYPE_F32));
    553             psBinaryOp(readout->variance, readout->variance, "*", psScalarAlloc(PS_SQR(renorm), PS_TYPE_F32));
    554         }
    555 
    556 }
     527        options->weightings->data.F32[index] = 1.0 / (psStatsGetValue(bg, PS_STAT_ROBUST_MEDIAN) * psImageCovarianceFactor(readout->covariance));
     528    } else {
     529        options->weightings->data.F32[index] = 1.0;
     530    }
     531    psLogMsg("psphotStack", PS_LOG_INFO, "Weighting for image %d is %f\n", index, options->weightings->data.F32[index]);
     532
     533    psFree(rng);
     534    psFree(bg);
     535    return true;
     536}
     537
     538# define NOISE_FRACTION 0.01             // Set minimum flux to this fraction of noise
     539# define SOURCE_MASK (PM_SOURCE_MODE_FAIL | PM_SOURCE_MODE_DEFECT | PM_SOURCE_MODE_SATURATED | PM_SOURCE_MODE_CR_LIMIT | PM_SOURCE_MODE_EXT_LIMIT) // Mask to apply to input sources
     540
     541// generate a fake readout against which to PSF match
     542pmReadout *makeFakeReadout(pmConfig *config, pmReadout *readoutRaw, psArray *sources, pmPSF *psf, psImageMaskType maskVal, int fullSize) {
     543
     544    pmReadout *fake = pmReadoutAlloc(NULL); // Fake readout with target PSF
     545
     546    psStats *bg = psStatsAlloc(PS_STAT_ROBUST_STDEV); // Statistics for background
     547    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
     548    if (!psImageBackground(bg, NULL, readoutRaw->image, readoutRaw->mask, maskVal, rng)) {
     549        psError(PSPHOT_ERR_DATA, false, "Can't measure background for image.");
     550        psFree(fake);
     551        psFree(bg);
     552        psFree(rng);
     553        return NULL;
     554    }
     555    float minFlux = NOISE_FRACTION * bg->robustStdev; // Minimum flux level for fake image
     556    psFree(rng);
     557    psFree(bg);
     558
     559    bool oldThreads = pmReadoutFakeThreads(true); // Old threading state
     560    if (!pmReadoutFakeFromSources(fake, readoutRaw->image->numCols, readoutRaw->image->numRows, sources, SOURCE_MASK, NULL, NULL, psf, minFlux, fullSize, false, true)) {
     561        psError(PSPHOT_ERR_DATA, false, "Unable to generate fake image with target PSF.");
     562        psFree(fake);
     563        return NULL;
     564    }
     565    pmReadoutFakeThreads(oldThreads);
     566
     567    fake->mask = psImageCopy(NULL, readoutRaw->mask, PS_TYPE_IMAGE_MASK);
     568
     569    // Add the background into the target image
     570    psImage *bgImage = stackBackgroundModel(readoutRaw, config); // Image of background
     571    psBinaryOp(fake->image, fake->image, "+", bgImage);
     572    psFree(bgImage);
     573
     574    return fake;
     575}
     576
     577// set the widths
     578psVector *SetOptWidths (bool *optimum, psMetadata *recipe) {
     579
     580    bool status;
     581
     582    *optimum = psMetadataLookupBool(&status, recipe, "OPTIMUM"); // Derive optimum parameters?
     583    psAssert (status, "missing recipe value %s", "OPTIMUM");
     584
     585    psVector *optWidths = NULL;         // Vector with FWHMs for optimum search
     586
     587    if (*optimum) {
     588        float optMin = psMetadataLookupF32(&status,  recipe, "OPTIMUM.MIN"); // Minimum width for search
     589        psAssert (status, "missing recipe value %s", "OPTIMUM.MIN");
     590       
     591        float optMax = psMetadataLookupF32(&status,  recipe, "OPTIMUM.MAX"); // Maximum width for search
     592        psAssert (status, "missing recipe value %s", "OPTIMUM.MAX");
     593       
     594        float optStep = psMetadataLookupF32(&status, recipe, "OPTIMUM.STEP"); // Step for search
     595        psAssert (status, "missing recipe value %s", "OPTIMUM.STEP");
     596
     597        optWidths = psVectorCreate(optWidths, optMin, optMax, optStep, PS_TYPE_F32);
     598    }
     599
     600    return optWidths;
     601}
  • branches/eam_branches/psphot.20100506/src/psphotStackParseCamera.c

    r27657 r27876  
    2525        psMetadata *input = item->data.md; // The input metadata of interest
    2626
    27         // look for 'IMAGE', 'MASK', 'VARIANCE' in folder (only 'IMAGE' is required)
    28 
    29         psString image = psMetadataLookupStr(NULL, input, "IMAGE"); // Name of image
    30         if (!image || strlen(image) == 0) {
    31             psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Component %s lacks IMAGE of type STR", item->name);
     27        pmFPAfile *rawInputFile = NULL;
     28        pmFPAfile *cnvInputFile = NULL;
     29
     30        // RAW (unconvolved) input data (RAW:IMAGE, RAW:MASK, RAW:VARIANCE, RAW:PSF)
     31        psString rawImage = psMetadataLookupStr(NULL, input, "RAW:IMAGE"); // Name of image
     32        if (rawImage && strlen(rawImage) > 0) {
     33            pmFPAfile *rawInputFile = defineFile(config, NULL, "PSPHOT.STACK.INPUT.RAW", rawImage, PM_FPA_FILE_IMAGE); // File for image
     34            if (!rawInputFile) {
     35                psError(PS_ERR_UNKNOWN, false, "Unable to define file from image %d (%s)", i, rawImage);
     36                return false;
     37            }
     38            psString mask = psMetadataLookupStr(&status, input, "RAW:MASK"); // Name of mask
     39            if (mask && strlen(mask) > 0) {
     40                if (!defineFile(config, rawInputFile, "PSPHOT.STACK.MASK.RAW", mask, PM_FPA_FILE_MASK)) {
     41                    psError(PS_ERR_UNKNOWN, false, "Unable to define file from mask %d (%s)", i, mask);
     42                    return false;
     43                }
     44            }
     45            psString variance = psMetadataLookupStr(&status, input, "RAW:VARIANCE"); // Name of variance map
     46            if (variance && strlen(variance) > 0) {
     47                if (!defineFile(config, rawInputFile, "PSPHOT.STACK.VARIANCE.RAW", variance, PM_FPA_FILE_VARIANCE)) {
     48                    psError(PS_ERR_UNKNOWN, false, "Unable to define file from variance %d (%s)", i, variance);
     49                    return false;
     50                }
     51            }
     52            psString psf = psMetadataLookupStr(&status, input, "RAW:PSF"); // Name of mask
     53            if (psf && strlen(psf) > 0) {
     54                if (!defineFile(config, rawInputFile, "PSPHOT.STACK.PSF.RAW", psf, PM_FPA_FILE_PSF)) {
     55                    psError(PS_ERR_UNKNOWN, false, "Unable to define file from psf %d (%s)", i, psf);
     56                    return false;
     57                }
     58            }
     59        }
     60
     61        // CNV (convolved) input data (CNV:IMAGE, CNV:MASK, CNV:VARIANCE, CNV:PSF)
     62        psString cnvImage = psMetadataLookupStr(NULL, input, "CNV:IMAGE"); // Name of image
     63        if (cnvImage && strlen(cnvImage) > 0) {
     64            pmFPAfile *cnvInputFile = defineFile(config, NULL, "PSPHOT.STACK.INPUT.CNV", cnvImage, PM_FPA_FILE_IMAGE); // File for image
     65            if (!cnvInputFile) {
     66                psError(PS_ERR_UNKNOWN, false, "Unable to define file from image %d (%s)", i, cnvImage);
     67                return false;
     68            }
     69            psString mask = psMetadataLookupStr(&status, input, "CNV:MASK"); // Name of mask
     70            if (mask && strlen(mask) > 0) {
     71                if (!defineFile(config, cnvInputFile, "PSPHOT.STACK.MASK.CNV", mask, PM_FPA_FILE_MASK)) {
     72                    psError(PS_ERR_UNKNOWN, false, "Unable to define file from mask %d (%s)", i, mask);
     73                    return false;
     74                }
     75            }
     76            psString variance = psMetadataLookupStr(&status, input, "CNV:VARIANCE"); // Name of variance map
     77            if (variance && strlen(variance) > 0) {
     78                if (!defineFile(config, cnvInputFile, "PSPHOT.STACK.VARIANCE.CNV", variance, PM_FPA_FILE_VARIANCE)) {
     79                    psError(PS_ERR_UNKNOWN, false, "Unable to define file from variance %d (%s)", i, variance);
     80                    return false;
     81                }
     82            }
     83            psString psf = psMetadataLookupStr(&status, input, "CNV:PSF"); // Name of mask
     84            if (psf && strlen(psf) > 0) {
     85                if (!defineFile(config, cnvInputFile, "PSPHOT.STACK.PSF.CNV", psf, PM_FPA_FILE_PSF)) {
     86                    psError(PS_ERR_UNKNOWN, false, "Unable to define file from psf %d (%s)", i, psf);
     87                    return false;
     88                }
     89            }
     90        }
     91
     92        if (!rawInputFile && !cnvInputFile) {
     93            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Component %s (%d) lacks IMAGE of type STR", item->name, i);
    3294            return false;
    3395        }
    34         pmFPAfile *imageFile = defineFile(config, NULL, "PSPHOT.INPUT", image, PM_FPA_FILE_IMAGE); // File for image
    35         if (!imageFile) {
    36             psError(PS_ERR_UNKNOWN, false, "Unable to define file from image %d (%s)", i, image);
    37             return false;
    38         }
    39 
    40         psString mask = psMetadataLookupStr(&status, input, "MASK"); // Name of mask
    41         if (mask && strlen(mask) > 0) {
    42             if (!defineFile(config, imageFile, "PSPHOT.MASK", mask, PM_FPA_FILE_MASK)) {
    43                 psError(PS_ERR_UNKNOWN, false, "Unable to define file from mask %d (%s)", i, mask);
    44                 return false;
    45             }
    46         }
    47 
    48         psString variance = psMetadataLookupStr(&status, input, "VARIANCE"); // Name of variance map
    49         if (variance && strlen(variance) > 0) {
    50             if (!defineFile(config, imageFile, "PSPHOT.VARIANCE", variance, PM_FPA_FILE_VARIANCE)) {
    51                 psError(PS_ERR_UNKNOWN, false, "Unable to define file from variance %d (%s)", i, variance);
    52                 return false;
    53             }
    54         }
    55         // the output sources are carried on the input->fpa structures
    56         pmFPAfile *outsources = pmFPAfileDefineOutputFromFile (config, imageFile, "PSPHOT.STACK.OUTPUT");
    57         if (!outsources) {
    58             psError(PSPHOT_ERR_CONFIG, false, "Cannot find a rule for PSPHOT.STACK.OUTPUT");
    59             return false;
    60         }
    61         outsources->save = true;
    62         outsources->fileID = i;         // this is used to generate output names
     96
     97        psString sources = psMetadataLookupStr(&status, input, "SOURCES"); // Name of mask
     98        pmFPAfile *srcInputFile = rawInputFile ? rawInputFile : cnvInputFile;
     99        if (sources && strlen(sources) > 0) {
     100            if (!defineFile(config, srcInputFile, "PSPHOT.STACK.SOURCES.CNV", sources, PM_FPA_FILE_CMF)) {
     101                psError(PS_ERR_UNKNOWN, false, "Unable to define file from sources %d (%s)", i, sources);
     102                return false;
     103            }
     104        }
     105
     106        // generate an pmFPAimage for the output convolved image
     107        // XXX output of these files should be optional
     108        {
     109            pmFPAfile *outputImage = pmFPAfileDefineOutput(config, NULL, "PSPHOT.STACK.OUTPUT.IMAGE");
     110            if (!outputImage) {
     111                psError(PSPHOT_ERR_CONFIG, false, "Trouble defining PSPHOT.STACK.OUTPUT.IMAGE");
     112                return false;
     113            }
     114            outputImage->save = true;
     115            outsources->fileID = i;             // this is used to generate output names
     116
     117            pmFPAfile *outputMask = pmFPAfileDefineOutput(config, outputImage->fpa, "PSPHOT.STACK.OUTPUT.MASK");
     118            if (!outputMask) {
     119                psError(PS_ERR_IO, false, _("Unable to generate output file from PSPHOT.STACK.OUTPUT.MASK"));
     120                return NULL;
     121            }
     122            if (outputMask->type != PM_FPA_FILE_MASK) {
     123                psError(PS_ERR_IO, true, "PSPHOT.STACK.OUTPUT.MASK is not of type MASK");
     124                return NULL;
     125            }
     126            outputMask->save = true;
     127
     128            pmFPAfile *outputVariance = pmFPAfileDefineOutput(config, outputImage->fpa, "PSPHOT.STACK.OUTPUT.VARIANCE");
     129            if (!outputVariance) {
     130                psError(PS_ERR_IO, false, _("Unable to generate output file from PSPHOT.STACK.OUTPUT.VARIANCE"));
     131                return NULL;
     132            }
     133            if (outputVariance->type != PM_FPA_FILE_VARIANCE) {
     134                psError(PS_ERR_IO, true, "PSPHOT.STACK.OUTPUT.VARIANCE is not of type VARIANCE");
     135                return NULL;
     136            }
     137            outputVariance->save = true;
     138
     139            // the output sources are carried on the outputImage->fpa structures
     140            pmFPAfile *outsources = pmFPAfileDefineOutputFromFile (config, outputImage, "PSPHOT.STACK.OUTPUT");
     141            if (!outsources) {
     142                psError(PSPHOT_ERR_CONFIG, false, "Cannot find a rule for PSPHOT.STACK.OUTPUT");
     143                return false;
     144            }
     145            outsources->save = true;
     146            outsources->fileID = i;             // this is used to generate output names
     147        }
    63148    }
    64149    psMetadataRemoveKey(config->arguments, "FILENAMES");
     
    71156
    72157    // generate an pmFPAimage for the chisqImage
    73     pmFPAfile *chisqImage = pmFPAfileDefineOutput(config, NULL, "PSPHOT.CHISQ.IMAGE");
    74     if (!chisqImage) {
    75         psError(PSPHOT_ERR_CONFIG, false, "Trouble defining PSPHOT.CHISQ.IMAGE");
    76         return false;
    77     }
    78     chisqImage->save = true;
    79 
    80     pmFPAfile *chisqMask = pmFPAfileDefineOutput(config, chisqImage->fpa, "PSPHOT.CHISQ.MASK");
    81     if (!chisqMask) {
    82         psError(PS_ERR_IO, false, _("Unable to generate output file from PSPHOT.CHISQ.MASK"));
    83         return NULL;
    84     }
    85     if (chisqMask->type != PM_FPA_FILE_MASK) {
    86         psError(PS_ERR_IO, true, "PSPHOT.CHISQ.MASK is not of type MASK");
    87         return NULL;
    88     }
    89     chisqMask->save = true;
    90 
    91     pmFPAfile *chisqVariance = pmFPAfileDefineOutput(config, chisqImage->fpa, "PSPHOT.CHISQ.VARIANCE");
    92     if (!chisqVariance) {
    93         psError(PS_ERR_IO, false, _("Unable to generate output file from PSPHOT.CHISQ.VARIANCE"));
    94         return NULL;
    95     }
    96     if (chisqVariance->type != PM_FPA_FILE_VARIANCE) {
    97         psError(PS_ERR_IO, true, "PSPHOT.CHISQ.VARIANCE is not of type VARIANCE");
    98         return NULL;
    99     }
    100     chisqVariance->save = true;
    101 
    102 # if (0)   
    103     // define the additional input/output files associated with psphot
    104     // XXX figure out which files are needed by psphotStack
    105     if (false && !psphotDefineFiles (config, input)) {
    106         psError(PSPHOT_ERR_CONFIG, false, "Trouble defining the additional input/output files");
    107         return false;
    108     }
    109 # endif
     158    // XXX output of these files should be optional
     159    {
     160        pmFPAfile *chisqImage = pmFPAfileDefineOutput(config, NULL, "PSPHOT.CHISQ.IMAGE");
     161        if (!chisqImage) {
     162            psError(PSPHOT_ERR_CONFIG, false, "Trouble defining PSPHOT.CHISQ.IMAGE");
     163            return false;
     164        }
     165        chisqImage->save = true;
     166
     167        pmFPAfile *chisqMask = pmFPAfileDefineOutput(config, chisqImage->fpa, "PSPHOT.CHISQ.MASK");
     168        if (!chisqMask) {
     169            psError(PS_ERR_IO, false, _("Unable to generate output file from PSPHOT.CHISQ.MASK"));
     170            return NULL;
     171        }
     172        if (chisqMask->type != PM_FPA_FILE_MASK) {
     173            psError(PS_ERR_IO, true, "PSPHOT.CHISQ.MASK is not of type MASK");
     174            return NULL;
     175        }
     176        chisqMask->save = true;
     177
     178        pmFPAfile *chisqVariance = pmFPAfileDefineOutput(config, chisqImage->fpa, "PSPHOT.CHISQ.VARIANCE");
     179        if (!chisqVariance) {
     180            psError(PS_ERR_IO, false, _("Unable to generate output file from PSPHOT.CHISQ.VARIANCE"));
     181            return NULL;
     182        }
     183        if (chisqVariance->type != PM_FPA_FILE_VARIANCE) {
     184            psError(PS_ERR_IO, true, "PSPHOT.CHISQ.VARIANCE is not of type VARIANCE");
     185            return NULL;
     186        }
     187        chisqVariance->save = true;
     188    }
    110189
    111190    psTrace("psphot", 1, "Done with psphotStackParseCamera...\n");
     
    145224}
    146225
     226
     227/***
     228 *
     229 *  psphotStack :
     230
     231 *    * inputs:
     232 *      * unconvolved images
     233 *      * raw convolved images
     234 *      * psfs (unconvolved or convolved?)
     235 *      * sources
     236 
     237 * optionally convolve the unconvolved or the raw inputs
     238 * optionally perform no convolutions
     239 * optionally save the psf-matched images
     240
     241 */
     242
     243   
     244# if (0)   
     245    // define the additional input/output files associated with psphot
     246    // XXX figure out which files are needed by psphotStack
     247    if (false && !psphotDefineFiles (config, input)) {
     248        psError(PSPHOT_ERR_CONFIG, false, "Trouble defining the additional input/output files");
     249        return false;
     250    }
     251# endif
     252
Note: See TracChangeset for help on using the changeset viewer.