diff options
author | Thomas White <taw@physics.org> | 2011-11-15 13:59:17 +0100 |
---|---|---|
committer | Thomas White <taw@physics.org> | 2012-02-22 15:27:40 +0100 |
commit | 25c3d29ed7701cadbb3813878f25b633a7cd7c2d (patch) | |
tree | 2efd3bd84ee6948543b0bc89053f7654047b8542 /libcrystfel/src/mosflm.c | |
parent | fb9df2f18def2d0b8fbdbc854c8a8c10e39ce6d9 (diff) |
Move indexing and rendering to libcrystfel
Diffstat (limited to 'libcrystfel/src/mosflm.c')
-rw-r--r-- | libcrystfel/src/mosflm.c | 609 |
1 files changed, 609 insertions, 0 deletions
diff --git a/libcrystfel/src/mosflm.c b/libcrystfel/src/mosflm.c new file mode 100644 index 00000000..5df5af21 --- /dev/null +++ b/libcrystfel/src/mosflm.c @@ -0,0 +1,609 @@ +/* + * mosflm.c + * + * Invoke the DPS auto-indexing algorithm through MOSFLM + * + * (c) 2010 Richard Kirian <rkirian@asu.edu> + * (c) 2006-2010 Thomas White <taw@physics.org> + * + * Part of CrystFEL - crystallography with a FEL + * + */ + +/* TODO: + * + * Properly read the newmat file (don't use fscanf-- spaces between numers + * are not guaranteed) + * + * "Success" is indicated by existence of NEWMAT file written by mosflm. + * Better to interact with mosflm directly in order to somehow verify success. + * + * Investigate how these keywords affect mosflms behavior: + * + * MOSAICITY + * DISPERSION + * DIVERGENCE + * POLARISATION + * POSTREF BEAM + * POSTREF USEBEAM OFF + * PREREFINE ON + * EXTRA ON + * POSTREF ON + * + * These did not seem to affect the results by my (Rick's) experience, probably + * because they are only used conjunction with image intensity data, but it's + * worth another look at the documentation. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + + +#include <stdlib.h> +#include <stdio.h> +#include <math.h> +#include <string.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <assert.h> +#include <fcntl.h> +#include <errno.h> + +#if HAVE_FORKPTY_LINUX +#include <pty.h> +#elif HAVE_FORKPTY_BSD +#include <util.h> +#endif + + +#include "image.h" +#include "mosflm.h" +#include "utils.h" +#include "peaks.h" + + +#define MOSFLM_VERBOSE 0 + + +typedef enum { + MOSFLM_INPUT_NONE, + MOSFLM_INPUT_LINE, + MOSFLM_INPUT_PROMPT +} MOSFLMInputType; + + +struct mosflm_data { + + /* MOSFLM auto-indexing low-level stuff */ + int pty; + pid_t pid; + char *rbuffer; + int rbufpos; + int rbuflen; + + /* MOSFLM high-level stuff */ + char newmatfile[128]; + char imagefile[128]; + char sptfile[128]; + int step; + int finished_ok; + UnitCell *target_cell; + +}; + + +static void mosflm_parseline(const char *line, struct image *image, + struct mosflm_data *dirax) +{ + #if MOSFLM_VERBOSE + char *copy; + int i; + + copy = strdup(line); + for ( i=0; i<strlen(copy); i++ ) { + if ( copy[i] == '\r' ) copy[i]='r'; + if ( copy[i] == '\n' ) copy[i]='\0'; + } + STATUS("MOSFLM: %s\n", copy); + free(copy); + #endif +} + + +static int read_newmat(const char *filename, struct image *image) +{ + FILE * fh; + float asx, asy, asz; + float bsx, bsy, bsz; + float csx, csy, csz; + int n; + double c; + + fh = fopen(filename, "r"); + if ( fh == NULL ) { + return 1; + } + n = fscanf(fh, "%f %f %f\n", &asx, &bsx, &csx); + n += fscanf(fh, "%f %f %f\n", &asy, &bsy, &csy); + n += fscanf(fh, "%f %f %f\n", &asz, &bsz, &csz); + if ( n != 9 ) { + STATUS("Fewer than 9 parameters found in NEWMAT file.\n"); + return 1; + } + fclose(fh); + + /* MOSFLM "A" matrix is multiplied by lambda, so fix this */ + c = 1/image->lambda; + + image->candidate_cells[0] = cell_new(); + + cell_set_reciprocal(image->candidate_cells[0], + asz*c, asy*c, asx*c, + bsz*c, bsy*c, bsx*c, + csz*c, csy*c, csx*c); + + image->ncells = 1; + + return 0; +} + + +/* Need to sort mosflm peaks by intensity... */ +struct sptline { + double x; /* x coordinate of peak */ + double y; /* y coordinate of peak */ + double h; /* height of peak */ + double s; /* sigma of peak */ +}; + + +static int compare_vals(const void *ap, const void *bp) +{ + const struct sptline a = *(struct sptline *)ap; + const struct sptline b = *(struct sptline *)bp; + + if ( a.h < b.h ) return 1; + if ( a.h > b.h ) return -1; + return 0; +} + + +/* Write .spt file for mosflm */ +static void write_spt(struct image *image, const char *filename) +{ + FILE *fh; + int i; + double fclen = 67.8; /* fake camera length in mm */ + double fpix = 0.075; /* fake pixel size in mm */ + double pix; + double height = 100.0; + double sigma = 1.0; + int nPeaks = image_feature_count(image->features); + struct sptline *sptlines; + + fh = fopen(filename, "w"); + if ( !fh ) { + ERROR("Couldn't open temporary file '%s'\n", filename); + return; + } + + fprintf(fh, "%10d %10d %10.8f %10.6f %10.6f\n", 1, 1, fpix, 1.0, 0.0); + fprintf(fh, "%10d %10d\n", 1, 1); + fprintf(fh, "%10.5f %10.5f\n", 0.0, 0.0); + + sptlines = malloc(sizeof(struct sptline)*nPeaks); + + for ( i=0; i<nPeaks; i++ ) { + + struct imagefeature *f; + struct panel *p; + double xs, ys, rx, ry; + + f = image_get_feature(image->features, i); + if ( f == NULL ) continue; + + p = find_panel(image->det, f->fs, f->ss); + if ( p == NULL ) continue; + + pix = 1000.0/p->res; /* pixel size in mm */ + height = f->intensity; + + xs = (f->fs-p->min_fs)*p->fsx + (f->ss-p->min_ss)*p->ssx; + ys = (f->ss-p->min_fs)*p->fsy + (f->ss-p->min_ss)*p->ssy; + rx = xs + p->cnx; + ry = ys + p->cny; + + sptlines[i].x = ry*pix*fclen/p->clen/1000.0; + sptlines[i].y = -rx*pix*fclen/p->clen/1000.0; + sptlines[i].h = height; + sptlines[i].s = sigma/1000.0; + + } + + qsort(sptlines, nPeaks, sizeof(struct sptline), compare_vals); + + for ( i=0; i<nPeaks; i++ ) { + + fprintf(fh, "%10.2f %10.2f %10.2f %10.2f %10.2f %10.2f\n", + sptlines[i].x, sptlines[i].y, + 0.0, 0.0, + sptlines[i].h, sptlines[i].s); + + } + + free(sptlines); + + fprintf(fh,"%10.2f %10.2f %10.2f %10.2f %10.2f %10.2f\n", + -999.0,-999.0,-999.0,-999.0,-999.0,-999.0); + fclose(fh); +} + + +/* Write a dummy 1x1 pixel image file for MOSFLM. Without post refinement, + * MOSFLM will ignore this, but it must be present. */ +static void write_img(struct image *image, const char *filename) +{ + FILE *fh; + unsigned short int *intimage; + + intimage = malloc(sizeof(unsigned short int)); + intimage[0] = 1; + + fh = fopen(filename, "w"); + if ( !fh ) { + ERROR("Couldn't open temporary file '%s'\n", filename); + return; + } + + /* Write header */ + fprintf(fh, "{\nHEADER_BYTES=512;\n"); + fprintf(fh, "BYTE_ORDER=little_endian;\n"); + fprintf(fh, "TYPE=unsigned_short;\n"); + fprintf(fh, "DIM=2;\n"); + fprintf(fh, "SIZE1=1;\n"); + fprintf(fh, "SIZE2=1;\n"); + fprintf(fh, "}\n"); + + /* Header padding */ + while ( ftell(fh) < 512 ) fprintf(fh," "); + + fwrite(intimage, sizeof(unsigned short int), 1, fh); + free(intimage); + fclose(fh); +} + + +static void mosflm_sendline(const char *line, struct mosflm_data *mosflm) +{ + #if MOSFLM_VERBOSE + char *copy; + int i; + + copy = strdup(line); + for ( i=0; i<strlen(copy); i++ ) { + if ( copy[i] == '\r' ) copy[i]='\0'; + if ( copy[i] == '\n' ) copy[i]='\0'; + } + STATUS("To MOSFLM: '%s'\n", copy); + free(copy); + #endif + + write(mosflm->pty, line, strlen(line)); +} + + +static void mosflm_send_next(struct image *image, struct mosflm_data *mosflm) +{ + char tmp[256]; + char symm[32]; + const char *sg; + double wavelength; + double a, b, c, alpha, beta, gamma; + int i, j; + + switch ( mosflm->step ) { + + case 1 : + mosflm_sendline("DETECTOR ROTATION HORIZONTAL" + " ANTICLOCKWISE ORIGIN LL FAST HORIZONTAL" + " RECTANGULAR\n", mosflm); + break; + + case 2 : + if ( mosflm->target_cell != NULL ) { + cell_get_parameters(mosflm->target_cell, &a, &b, &c, + &alpha, &beta, &gamma); + snprintf(tmp, 255, + "CELL %6.2f %6.2f %6.2f %6.2f %6.2f %6.2f\n", + a*1e10, b*1e10, c*1e10, + rad2deg(alpha), rad2deg(beta), rad2deg(gamma)); + mosflm_sendline(tmp, mosflm); + } else { + mosflm_sendline("# Do nothing\n", mosflm); + } + break; + + case 3 : + if ( mosflm->target_cell != NULL ) { + sg = cell_get_spacegroup(mosflm->target_cell); + /* Remove white space from space group */ + j = 0; + for ( i=0; i<strlen(sg); i++ ) { + if (sg[i] != ' ') { + symm[j++] = sg[i]; + } + } + symm[j] = '\0'; + snprintf(tmp, 255, "SYMM %s\n", symm); + mosflm_sendline(tmp, mosflm); + } else { + mosflm_sendline("SYMM P1\n", mosflm); + } + break; + + case 4 : + mosflm_sendline("DISTANCE 67.8\n", mosflm); + break; + + case 5 : + mosflm_sendline("BEAM 0.0 0.0\n", mosflm); + break; + + case 6 : + wavelength = image->lambda*1e10; + snprintf(tmp, 255, "WAVELENGTH %10.5f\n", wavelength); + mosflm_sendline(tmp, mosflm); + break; + + case 7 : + snprintf(tmp, 255, "NEWMAT %s\n", mosflm->newmatfile); + mosflm_sendline(tmp, mosflm); + break; + + case 8 : + snprintf(tmp, 255, "IMAGE %s phi 0 0\n", mosflm->imagefile); + mosflm_sendline(tmp, mosflm); + break; + + case 9 : + snprintf(tmp, 255, "AUTOINDEX DPS FILE %s" + " IMAGE 1 MAXCELL 1000 REFINE\n", + mosflm->sptfile); + + /* "This option is only available if you e-mail Andrew Leslie + * and ask for it." - MOSFLM + * snprintf(tmp, 255, "AUTOINDEX NODISPLAY IMAGE 1 FILE %s\n", + * mosflm->sptfile); */ + mosflm_sendline(tmp, mosflm); + break; + + case 10 : + mosflm_sendline("GO\n", mosflm); + mosflm->finished_ok = 1; + break; + + default: + mosflm_sendline("exit\n", mosflm); + return; + + } + + mosflm->step++; +} + + +static int mosflm_readable(struct image *image, struct mosflm_data *mosflm) +{ + int rval; + int no_string = 0; + + rval = read(mosflm->pty, mosflm->rbuffer+mosflm->rbufpos, + mosflm->rbuflen-mosflm->rbufpos); + + if ( (rval == -1) || (rval == 0) ) return 1; + + mosflm->rbufpos += rval; + assert(mosflm->rbufpos <= mosflm->rbuflen); + + while ( (!no_string) && (mosflm->rbufpos > 0) ) { + + int i; + int block_ready = 0; + MOSFLMInputType type = MOSFLM_INPUT_NONE; + + /* See if there's a full line in the buffer yet */ + for ( i=0; i<mosflm->rbufpos-1; i++ ) { + /* Means the last value looked at is rbufpos-2 */ + + /* Is there a prompt in the buffer? */ + if ( (i+10 <= mosflm->rbufpos) + && (!strncmp(mosflm->rbuffer+i, "MOSFLM => ", 10)) ) { + block_ready = 1; + type = MOSFLM_INPUT_PROMPT; + break; + } + + if ( (mosflm->rbuffer[i] == '\r') + && (mosflm->rbuffer[i+1] == '\n') ) { + block_ready = 1; + type = MOSFLM_INPUT_LINE; + break; + } + + } + + if ( block_ready ) { + + unsigned int new_rbuflen; + unsigned int endbit_length; + char *block_buffer = NULL; + + switch ( type ) { + + case MOSFLM_INPUT_LINE : + + block_buffer = malloc(i+1); + memcpy(block_buffer, mosflm->rbuffer, i); + block_buffer[i] = '\0'; + + if ( block_buffer[0] == '\r' ) { + memmove(block_buffer, block_buffer+1, i); + } + + mosflm_parseline(block_buffer, image, mosflm); + free(block_buffer); + endbit_length = i+2; + break; + + case MOSFLM_INPUT_PROMPT : + + mosflm_send_next(image, mosflm); + endbit_length = i+7; + break; + + default : + + /* Obviously, this never happens :) */ + ERROR("Unrecognised MOSFLM input mode! " + "I don't know how to understand MOSFLM\n"); + return 1; + + } + + /* Now the block's been parsed, it should be + * forgotten about */ + memmove(mosflm->rbuffer, + mosflm->rbuffer + endbit_length, + mosflm->rbuflen - endbit_length); + + /* Subtract the number of bytes removed */ + mosflm->rbufpos = mosflm->rbufpos + - endbit_length; + new_rbuflen = mosflm->rbuflen - endbit_length; + if ( new_rbuflen == 0 ) new_rbuflen = 256; + mosflm->rbuffer = realloc(mosflm->rbuffer, + new_rbuflen); + mosflm->rbuflen = new_rbuflen; + + } else { + + if ( mosflm->rbufpos==mosflm->rbuflen ) { + + /* More buffer space is needed */ + mosflm->rbuffer = realloc( + mosflm->rbuffer, + mosflm->rbuflen + 256); + mosflm->rbuflen = mosflm->rbuflen + 256; + /* The new space gets used at the next + * read, shortly... */ + + } + no_string = 1; + + } + + } + + return 0; +} + + +void run_mosflm(struct image *image, UnitCell *cell) +{ + struct mosflm_data *mosflm; + unsigned int opts; + int status; + int rval; + + mosflm = malloc(sizeof(struct mosflm_data)); + if ( mosflm == NULL ) { + ERROR("Couldn't allocate memory for MOSFLM data.\n"); + return; + } + + mosflm->target_cell = cell; + + snprintf(mosflm->imagefile, 127, "xfel-%i_001.img", image->id); + write_img(image, mosflm->imagefile); /* Dummy image */ + + snprintf(mosflm->sptfile, 127, "xfel-%i_001.spt", image->id); + write_spt(image, mosflm->sptfile); + + snprintf(mosflm->newmatfile, 127, "xfel-%i.newmat", image->id); + remove(mosflm->newmatfile); + + mosflm->pid = forkpty(&mosflm->pty, NULL, NULL, NULL); + if ( mosflm->pid == -1 ) { + ERROR("Failed to fork for MOSFLM\n"); + free(mosflm); + return; + } + if ( mosflm->pid == 0 ) { + + /* Child process: invoke MOSFLM */ + struct termios t; + + /* Turn echo off */ + tcgetattr(STDIN_FILENO, &t); + t.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); + tcsetattr(STDIN_FILENO, TCSANOW, &t); + + execlp("ipmosflm", "", (char *)NULL); + ERROR("Failed to invoke MOSFLM.\n"); + _exit(0); + + } + + mosflm->rbuffer = malloc(256); + mosflm->rbuflen = 256; + mosflm->rbufpos = 0; + + /* Set non-blocking */ + opts = fcntl(mosflm->pty, F_GETFL); + fcntl(mosflm->pty, F_SETFL, opts | O_NONBLOCK); + + mosflm->step = 1; /* This starts the "initialisation" procedure */ + mosflm->finished_ok = 0; + + do { + + fd_set fds; + struct timeval tv; + int sval; + + FD_ZERO(&fds); + FD_SET(mosflm->pty, &fds); + + tv.tv_sec = 10; + tv.tv_usec = 0; + + sval = select(mosflm->pty+1, &fds, NULL, NULL, &tv); + + if ( sval == -1 ) { + int err = errno; + ERROR("select() failed: %s\n", strerror(err)); + rval = 1; + } else if ( sval != 0 ) { + rval = mosflm_readable(image, mosflm); + } else { + ERROR("No response from MOSFLM..\n"); + rval = 1; + } + + } while ( !rval ); + + close(mosflm->pty); + free(mosflm->rbuffer); + waitpid(mosflm->pid, &status, 0); + + if ( mosflm->finished_ok == 0 ) { + ERROR("MOSFLM doesn't seem to be working properly.\n"); + } else { + /* Read the mosflm NEWMAT file and get cell if found */ + read_newmat(mosflm->newmatfile, image); + } + + free(mosflm); +} |