aboutsummaryrefslogtreecommitdiff
path: root/libcrystfel/src/mosflm.c
diff options
context:
space:
mode:
authorThomas White <taw@physics.org>2011-11-15 13:59:17 +0100
committerThomas White <taw@physics.org>2012-02-22 15:27:40 +0100
commit25c3d29ed7701cadbb3813878f25b633a7cd7c2d (patch)
tree2efd3bd84ee6948543b0bc89053f7654047b8542 /libcrystfel/src/mosflm.c
parentfb9df2f18def2d0b8fbdbc854c8a8c10e39ce6d9 (diff)
Move indexing and rendering to libcrystfel
Diffstat (limited to 'libcrystfel/src/mosflm.c')
-rw-r--r--libcrystfel/src/mosflm.c609
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);
+}