/* * indexamajig.c * * Find hits, index patterns, output hkl+intensity etc. * * (c) 2006-2010 Thomas White * * Part of CrystFEL - crystallography with a FEL * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "hdf5-file.h" #include "index.h" #include "peaks.h" #include "diffraction.h" #include "diffraction-gpu.h" #include "detector.h" #include "sfac.h" #include "filters.h" #include "reflections.h" #define MAX_THREADS (96) enum { PEAK_ZAEF, PEAK_HDF5, }; struct process_args { /* Input */ char *filename; int id; pthread_mutex_t *gpu_mutex; /* Protects "gctx" */ UnitCell *cell; int config_cmfilter; int config_noisefilter; int config_writedrx; int config_dumpfound; int config_verbose; int config_alternate; int config_nearbragg; int config_gpu; int config_simulate; int config_nomatch; int config_polar; int config_sanity; int config_satcorr; int config_sa; int config_closer; float threshold; struct detector *det; IndexingMethod indm; IndexingPrivate *ipriv; const double *intensities; struct gpu_context *gctx; int peaks; /* Thread control and output */ pthread_mutex_t control_mutex; /* Protects the scary stuff below */ int start; int finish; int done; int indexable; int sane; /* Output stream */ pthread_mutex_t *output_mutex; /* Protects the output stream */ FILE *ofh; }; static void show_help(const char *s) { printf("Syntax: %s [options]\n\n", s); printf( "Process and index FEL diffraction images.\n" "\n" " -h, --help Display this help message.\n" "\n" " -i, --input= Specify file containing list of images to process.\n" " '-' means stdin, which is the default.\n" "\n" " --indexing= Use 'method' for indexing. Choose from:\n" " none : no indexing (default)\n" " dirax : invoke DirAx\n" " template : index by template matching\n" " -g. --geometry= Get detector geometry from file.\n" " -p, --pdb= PDB file from which to get the unit cell to match.\n" " Default: 'molecule.pdb'.\n" " -x, --prefix=

Prefix filenames from input file with

.\n" " --peaks= Use 'method' for finding peaks. Choose from:\n" " zaef : Use Zaefferer (2000) gradient detection.\n" " This is the default method.\n" " hdf5 : Get from /processing/hitfinder/peakinfo\n" " in the HDF5 file.\n" "\n" "\nWith just the above options, this program does not do much of practical use." "\nYou should also enable some of the following:\n\n" " --near-bragg Output a list of reflection intensities to stdout.\n" " When pixels with fractional indices within 0.1 of\n" " integer values (the Bragg condition) are found,\n" " the integral of pixels within a ten pixel radius\n" " of the nearest-to-Bragg pixel will be reported as\n" " the intensity. The centroid of the pixels will\n" " be given as the coordinates, as well as the h,k,l\n" " (integer) indices of the reflection. If a peak\n" " was located by the initial peak search close to\n" " the \"near Bragg\" location, its coordinates will\n" " be taken as the centre instead.\n" " --simulate Simulate the diffraction pattern using the indexed\n" " unit cell. The simulated pattern will be saved\n" " as \"simulated.h5\". You can TRY to combine this\n" " with \"-j \" with n greater than 1, but it's\n" " not a good idea.\n" " --write-drx Write 'xfel.drx' for visualisation of reciprocal\n" " space. Implied by any indexing method other than\n" " 'none'. Beware: the units in this file are\n" " reciprocal Angstroms.\n" " --dump-peaks Write the results of the peak search to stdout.\n" " The intensities in this list are from the\n" " centroid/integration procedure.\n" "\n" "\nFor more control over the process, you might need:\n\n" " --no-match Don't attempt to match the indexed cell to the\n" " model, just proceed with the one generated by the\n" " auto-indexing procedure.\n" " --check-sanity Check that indexed locations approximately correspond\n" " with detected peaks.\n" " --filter-cm Perform common-mode noise subtraction on images\n" " before proceeding. Intensities will be extracted\n" " from the image as it is after this processing.\n" " --filter-noise Apply an aggressive noise filter which sets all\n" " pixels in each 3x3 region to zero if any of them\n" " have negative values. Intensity measurement will\n" " be performed on the image as it was before this.\n" " --unpolarized Don't correct for the polarisation of the X-rays.\n" " --no-sat-corr Don't correct values of saturated peaks using a\n" " table included in the HDF5 file.\n" " --no-sa Don't correct for the differing solid angles of\n" " the pixels.\n" " --threshold= Only accept peaks above ADU. Default: 800.\n" "\n" "\nIf you used --simulate, you may also want:\n\n" " --intensities= Specify file containing reflection intensities\n" " to use when simulating.\n" "\n" "\nOptions for greater performance or verbosity:\n\n" " --verbose Be verbose about indexing.\n" " --gpu Use the GPU to speed up the simulation.\n" " -j Run analyses in parallel. Default 1.\n" "\n" "\nOptions you probably won't need:\n\n" " --no-check-prefix Don't attempt to correct the --prefix.\n" " --no-closer-peak Don't integrate from the location of a nearby peak\n" " instead of the position closest to the reciprocal\n" " lattice point.\n" ); } static struct image *get_simage(struct image *template, int alternate) { struct image *image; struct panel panels[2]; image = malloc(sizeof(*image)); /* Simulate a diffraction pattern */ image->twotheta = NULL; image->data = NULL; image->det = template->det; image->flags = NULL; image->f0_available = 0; image->f0 = 1.0; /* View head-on (unit cell is tilted) */ image->orientation.w = 1.0; image->orientation.x = 0.0; image->orientation.y = 0.0; image->orientation.z = 0.0; /* Detector geometry for the simulation * - not necessarily the same as the original. */ image->width = 1024; image->height = 1024; image->det->n_panels = 2; if ( alternate ) { /* Upper */ panels[0].min_x = 0; panels[0].max_x = 1023; panels[0].min_y = 512; panels[0].max_y = 1023; panels[0].cx = 523.6; panels[0].cy = 502.5; panels[0].clen = 56.4e-2; /* 56.4 cm */ panels[0].res = 13333.3; /* 75 microns/pixel */ /* Lower */ panels[1].min_x = 0; panels[1].max_x = 1023; panels[1].min_y = 0; panels[1].max_y = 511; panels[1].cx = 520.8; panels[1].cy = 525.0; panels[1].clen = 56.7e-2; /* 56.7 cm */ panels[1].res = 13333.3; /* 75 microns/pixel */ image->det->panels = panels; } else { /* Copy pointer to old geometry */ image->det->panels = template->det->panels; } image->lambda = ph_en_to_lambda(eV_to_J(1.8e3)); image->features = template->features; image->filename = template->filename; image->indexed_cell = template->indexed_cell; image->f0 = template->f0; /* Prevent muppetry */ image->hits = NULL; image->n_hits = 0; return image; } static void simulate_and_write(struct image *simage, struct gpu_context **gctx, const double *intensities, UnitCell *cell) { /* Set up GPU if necessary */ if ( (gctx != NULL) && (*gctx == NULL) ) { *gctx = setup_gpu(0, simage, intensities); } if ( (gctx != NULL) && (*gctx != NULL) ) { get_diffraction_gpu(*gctx, simage, 24, 24, 40, cell); } else { get_diffraction(simage, 24, 24, 40, intensities, NULL, cell, 0, GRADIENT_MOSAIC); } record_image(simage, 0); hdf5_write("simulated.h5", simage->data, simage->width, simage->height, H5T_NATIVE_FLOAT); } static void process_image(struct process_args *pargs) { struct hdfile *hdfile; struct image image; struct image *simage; float *data_for_measurement; size_t data_size; const char *filename = pargs->filename; UnitCell *cell = pargs->cell; int config_cmfilter = pargs->config_cmfilter; int config_noisefilter = pargs->config_noisefilter; int config_writedrx = pargs->config_writedrx; int config_dumpfound = pargs->config_dumpfound; int config_verbose = pargs->config_verbose; int config_alternate = pargs->config_alternate; int config_nearbragg = pargs->config_nearbragg; int config_gpu = pargs->config_gpu; int config_simulate = pargs->config_simulate; int config_nomatch = pargs->config_nomatch; int config_polar = pargs->config_polar; IndexingMethod indm = pargs->indm; const double *intensities = pargs->intensities; struct gpu_context *gctx = pargs->gctx; image.features = NULL; image.data = NULL; image.indexed_cell = NULL; image.id = pargs->id; image.filename = filename; image.hits = NULL; image.n_hits = 0; image.det = pargs->det; /* View head-on (unit cell is tilted) */ image.orientation.w = 1.0; image.orientation.x = 0.0; image.orientation.y = 0.0; image.orientation.z = 0.0; STATUS("Processing '%s'\n", image.filename); pargs->sane = 0; pargs->indexable = 0; hdfile = hdfile_open(filename); if ( hdfile == NULL ) { return; } else if ( hdfile_set_first_image(hdfile, "/") ) { ERROR("Couldn't select path\n"); return; } hdf5_read(hdfile, &image, pargs->config_satcorr); if ( config_cmfilter ) { filter_cm(&image); } /* Take snapshot of image after CM subtraction but before * the aggressive noise filter. */ data_size = image.width*image.height*sizeof(float); data_for_measurement = malloc(data_size); if ( config_noisefilter ) { filter_noise(&image, data_for_measurement); } else { memcpy(data_for_measurement, image.data, data_size); } switch ( pargs->peaks ) { case PEAK_HDF5 : /* Get peaks from HDF5 */ if ( get_peaks(&image, hdfile) ) { ERROR("Failed to get peaks from HDF5 file.\n"); return; } break; case PEAK_ZAEF : search_peaks(&image, pargs->threshold); break; } /* Get rid of noise-filtered version at this point * - it was strictly for the purposes of peak detection. */ free(image.data); image.data = data_for_measurement; if ( config_dumpfound ) { dump_peaks(&image, pargs->ofh, pargs->output_mutex); } /* Not indexing nor writing xfel.drx? * Then there's nothing left to do. */ if ( (!config_writedrx) && (indm == INDEXING_NONE) ) { goto done; } /* Calculate orientation matrix (by magic) */ if ( config_writedrx || (indm != INDEXING_NONE) ) { index_pattern(&image, cell, indm, config_nomatch, config_verbose, pargs->ipriv); } /* No cell at this point? Then we're done. */ if ( image.indexed_cell == NULL ) goto done; pargs->indexable = 1; /* Sanity check */ if ( pargs->config_sanity && !peak_sanity_check(&image, image.indexed_cell, 0, 0.1) ) { STATUS("Failed peak sanity check.\n"); goto done; } else { pargs->sane = 1; } /* Measure intensities if requested */ if ( config_nearbragg ) { output_intensities(&image, image.indexed_cell, pargs->output_mutex, config_polar, pargs->config_sa, pargs->config_closer, pargs->ofh, 0, 0.1); } simage = get_simage(&image, config_alternate); /* Simulate if requested */ if ( config_simulate ) { if ( config_gpu ) { pthread_mutex_lock(pargs->gpu_mutex); simulate_and_write(simage, &gctx, intensities, image.indexed_cell); pthread_mutex_unlock(pargs->gpu_mutex); } else { simulate_and_write(simage, NULL, intensities, image.indexed_cell); } } /* Finished with alternate image */ if ( simage->twotheta != NULL ) free(simage->twotheta); if ( simage->data != NULL ) free(simage->data); free(simage); /* Only free cell if found */ cell_free(image.indexed_cell); done: free(image.data); free(image.flags); image_feature_list_free(image.features); free(image.hits); hdfile_close(hdfile); } static void *worker_thread(void *pargsv) { struct process_args *pargs = pargsv; int finish; do { int wakeup; process_image(pargs); pthread_mutex_lock(&pargs->control_mutex); pargs->done = 1; pargs->start = 0; pthread_mutex_unlock(&pargs->control_mutex); /* Go to sleep until told to exit or process next image */ do { pthread_mutex_lock(&pargs->control_mutex); /* Either of these can result in the thread waking up */ wakeup = pargs->start || pargs->finish; finish = pargs->finish; pthread_mutex_unlock(&pargs->control_mutex); usleep(20000); } while ( !wakeup ); } while ( !pargs->finish ); return NULL; } int main(int argc, char *argv[]) { int c; struct gpu_context *gctx = NULL; char *filename = NULL; char *outfile = NULL; FILE *fh; FILE *ofh; char *rval = NULL; int n_images; int n_indexable; int n_sane; int config_noindex = 0; int config_dumpfound = 0; int config_nearbragg = 0; int config_writedrx = 0; int config_simulate = 0; int config_cmfilter = 0; int config_noisefilter = 0; int config_nomatch = 0; int config_gpu = 0; int config_verbose = 0; int config_alternate = 0; int config_polar = 1; int config_sanity = 0; int config_satcorr = 1; int config_sa = 1; int config_checkprefix = 1; int config_closer = 1; float threshold = 800.0; struct detector *det; char *geometry = NULL; IndexingMethod indm; char *indm_str = NULL; UnitCell *cell; double *intensities = NULL; char *intfile = NULL; char *pdb = NULL; char *prefix = NULL; char *speaks = NULL; int peaks; int nthreads = 1; pthread_t workers[MAX_THREADS]; struct process_args *worker_args[MAX_THREADS]; int worker_active[MAX_THREADS]; int i; pthread_mutex_t output_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t gpu_mutex = PTHREAD_MUTEX_INITIALIZER; char prepare_line[1024]; char prepare_filename[1024]; IndexingPrivate *ipriv; /* Long options */ const struct option longopts[] = { {"help", 0, NULL, 'h'}, {"input", 1, NULL, 'i'}, {"output", 1, NULL, 'o'}, {"gpu", 0, &config_gpu, 1}, {"no-index", 0, &config_noindex, 1}, {"dump-peaks", 0, &config_dumpfound, 1}, {"peaks", 1, NULL, 2}, {"near-bragg", 0, &config_nearbragg, 1}, {"write-drx", 0, &config_writedrx, 1}, {"indexing", 1, NULL, 'z'}, {"geometry", 1, NULL, 'g'}, {"simulate", 0, &config_simulate, 1}, {"filter-cm", 0, &config_cmfilter, 1}, {"filter-noise", 0, &config_noisefilter, 1}, {"no-match", 0, &config_nomatch, 1}, {"verbose", 0, &config_verbose, 1}, {"alternate", 0, &config_alternate, 1}, {"intensities", 1, NULL, 'q'}, {"pdb", 1, NULL, 'p'}, {"prefix", 1, NULL, 'x'}, {"unpolarized", 0, &config_polar, 0}, {"check-sanity", 0, &config_sanity, 1}, {"no-sat-corr", 0, &config_satcorr, 0}, {"sat-corr", 0, &config_satcorr, 1}, /* Compat */ {"no-sa", 0, &config_sa, 0}, {"threshold", 1, NULL, 't'}, {"no-check-prefix", 0, &config_checkprefix, 0}, {"no-closer-peak", 0, &config_closer, 0}, {0, 0, NULL, 0} }; /* Short options */ while ((c = getopt_long(argc, argv, "hi:wp:j:x:g:t:o:", longopts, NULL)) != -1) { switch (c) { case 'h' : show_help(argv[0]); return 0; case 'i' : filename = strdup(optarg); break; case 'o' : outfile = strdup(optarg); break; case 'z' : indm_str = strdup(optarg); break; case 'q' : intfile = strdup(optarg); break; case 'p' : pdb = strdup(optarg); break; case 'x' : prefix = strdup(optarg); break; case 'j' : nthreads = atoi(optarg); break; case 'g' : geometry = strdup(optarg); break; case 't' : threshold = strtof(optarg, NULL); break; case 2 : speaks = strdup(optarg); break; case 0 : break; default : return 1; } } if ( filename == NULL ) { filename = strdup("-"); } if ( strcmp(filename, "-") == 0 ) { fh = stdin; } else { fh = fopen(filename, "r"); } if ( fh == NULL ) { ERROR("Failed to open input file '%s'\n", filename); return 1; } free(filename); if ( outfile == NULL ) { outfile = strdup("-"); } if ( strcmp(outfile, "-") == 0 ) { ofh = stdout; } else { ofh = fopen(outfile, "w"); } if ( ofh == NULL ) { ERROR("Failed to open output file '%s'\n", outfile); return 1; } free(outfile); if ( speaks == NULL ) { speaks = strdup("zaef"); STATUS("You didn't specify a peak detection method.\n"); STATUS("I'm using 'zaef' for you.\n"); } if ( strcmp(speaks, "zaef") == 0 ) { peaks = PEAK_ZAEF; } else if ( strcmp(speaks, "hdf5") == 0 ) { peaks = PEAK_HDF5; } else { ERROR("Unrecognised peak detection method '%s'\n", speaks); return 1; } if ( intfile != NULL ) { ReflItemList *items; items = read_reflections(intfile, intensities, NULL, NULL, NULL); delete_items(items); } else { intensities = NULL; } if ( pdb == NULL ) { pdb = strdup("molecule.pdb"); } if ( prefix == NULL ) { prefix = strdup(""); } else { if ( config_checkprefix ) { prefix = check_prefix(prefix); } } if ( (nthreads == 0) || (nthreads > MAX_THREADS) ) { ERROR("Invalid number of threads.\n"); return 1; } if ( indm_str == NULL ) { STATUS("You didn't specify an indexing method, so I won't" " try to index anything.\n" "If that isn't what you wanted, re-run with" " --indexing=.\n"); indm = INDEXING_NONE; } else if ( strcmp(indm_str, "none") == 0 ) { indm = INDEXING_NONE; } else if ( strcmp(indm_str, "dirax") == 0) { indm = INDEXING_DIRAX; } else if ( strcmp(indm_str, "template") == 0) { indm = INDEXING_TEMPLATE; } else { ERROR("Unrecognised indexing method '%s'\n", indm_str); return 1; } free(indm_str); if ( geometry == NULL ) { ERROR("You need to specify a geometry file with --geometry\n"); return 1; } det = get_detector_geometry(geometry); if ( det == NULL ) { ERROR("Failed to read detector geometry from '%s'\n", geometry); return 1; } free(geometry); if ( (!config_nomatch) || (indm == INDEXING_TEMPLATE) ) { cell = load_cell_from_pdb(pdb); if ( cell == NULL ) { ERROR("Couldn't read unit cell (from %s)\n", pdb); return 1; } } else { STATUS("No cell needed because --no-match was used.\n"); cell = NULL; } free(pdb); /* Start by writing the entire command line to stdout */ fprintf(ofh, "Command line:"); for ( i=0; ifilename = malloc(1024); worker_args[i]->ofh = ofh; worker_args[i]->peaks = peaks; worker_active[i] = 0; } /* Start threads off */ for ( i=0; i 0 ) { strcpy(line, prepare_line); prepare_line[0] = '\0'; } else { rval = fgets(line, 1023, fh); if ( rval == NULL ) continue; } chomp(line); snprintf(pargs->filename, 1023, "%s%s", prefix, line); n_images++; pargs->output_mutex = &output_mutex; pargs->gpu_mutex = &gpu_mutex; pthread_mutex_init(&pargs->control_mutex, NULL); pargs->config_cmfilter = config_cmfilter; pargs->config_noisefilter = config_noisefilter; pargs->config_writedrx = config_writedrx; pargs->config_dumpfound = config_dumpfound; pargs->config_verbose = config_verbose; pargs->config_alternate = config_alternate; pargs->config_nearbragg = config_nearbragg; pargs->config_gpu = config_gpu; pargs->config_simulate = config_simulate; pargs->config_nomatch = config_nomatch; pargs->config_polar = config_polar; pargs->config_sanity = config_sanity; pargs->config_satcorr = config_satcorr; pargs->config_sa = config_sa; pargs->config_closer = config_closer; pargs->cell = cell; pargs->det = det; pargs->ipriv = ipriv; pargs->indm = indm; pargs->intensities = intensities; pargs->gctx = gctx; pargs->threshold = threshold; pargs->id = i; pthread_mutex_lock(&pargs->control_mutex); pargs->done = 0; pargs->start = 1; pargs->finish = 0; pthread_mutex_unlock(&pargs->control_mutex); worker_active[i] = 1; r = pthread_create(&workers[i], NULL, worker_thread, pargs); if ( r != 0 ) { worker_active[i] = 0; ERROR("Couldn't start thread %i\n", i); } } /* Keep threads busy until the end of the data */ do { int i; for ( i=0; icontrol_mutex); done = pargs->done; pthread_mutex_unlock(&pargs->control_mutex); if ( !done ) continue; /* Results will be processed after checking if * there are any more images to process. */ /* Get next filename */ rval = fgets(line, 1023, fh); /* In this case, the result of the last file * file will be processed when the thread is * joined. */ if ( rval == NULL ) break; /* Record the result */ n_indexable += pargs->indexable; n_sane += pargs->sane; chomp(line); snprintf(pargs->filename, 1023, "%s%s", prefix, line); n_images++; /* Wake the thread up ... */ pthread_mutex_lock(&pargs->control_mutex); pargs->done = 0; pargs->start = 1; pthread_mutex_unlock(&pargs->control_mutex); } } while ( rval != NULL ); /* Join threads */ for ( i=0; icontrol_mutex); pargs->finish = 1; pthread_mutex_unlock(&pargs->control_mutex); /* Wait for it to join */ pthread_join(workers[i], NULL); worker_active[i] = 0; n_indexable += pargs->indexable; n_sane += pargs->sane; free: if ( worker_args[i]->filename != NULL ) { free(worker_args[i]->filename); } free(worker_args[i]); } cleanup_indexing(ipriv); free(prefix); free(det->panels); free(det); cell_free(cell); fclose(fh); STATUS("There were %i images. %i could be indexed, of which %i" " looked sane.\n", n_images, n_indexable, n_sane); if ( gctx != NULL ) { cleanup_gpu(gctx); } return 0; }