indexamajig.c 30.9 KB
Newer Older
Thomas White's avatar
Thomas White committed
1
/*
2
 * indexamajig.c
Thomas White's avatar
Thomas White committed
3
 *
Thomas White's avatar
Thomas White committed
4
 * Index patterns, output hkl+intensity etc.
Thomas White's avatar
Thomas White committed
5
 *
6
7
 * Copyright © 2012 Deutsches Elektronen-Synchrotron DESY,
 *                  a research centre of the Helmholtz Association.
8
9
 * Copyright © 2012 Richard Kirian
 * Copyright © 2012 Lorenzo Galli
10
11
 *
 * Authors:
12
13
14
 *   2010-2012 Thomas White <taw@physics.org>
 *   2011      Richard Kirian
 *   2012      Lorenzo Galli
Chuck's avatar
Chuck committed
15
 *   2012      Chunhong Yoon
Thomas White's avatar
Thomas White committed
16
 *
Thomas White's avatar
Thomas White committed
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 * This file is part of CrystFEL.
 *
 * CrystFEL is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * CrystFEL is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with CrystFEL.  If not, see <http://www.gnu.org/licenses/>.
Thomas White's avatar
Thomas White committed
31
32
33
34
35
36
37
38
39
40
41
42
43
44
 *
 */


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
Thomas White's avatar
Thomas White committed
45
#include <hdf5.h>
Thomas White's avatar
Thomas White committed
46
#include <gsl/gsl_errno.h>
47
#include <pthread.h>
Chuck's avatar
Chuck committed
48
49
#include <sys/wait.h>
#include <fcntl.h>
50
51

#ifdef HAVE_CLOCK_GETTIME
52
#include <time.h>
53
54
55
#else
#include <sys/time.h>
#endif
Thomas White's avatar
Thomas White committed
56

Thomas White's avatar
Thomas White committed
57
58
59
60
61
62
63
64
65
66
67
#include "utils.h"
#include "hdf5-file.h"
#include "index.h"
#include "peaks.h"
#include "detector.h"
#include "filters.h"
#include "thread-pool.h"
#include "beam-parameters.h"
#include "geometry.h"
#include "stream.h"
#include "reflist-utils.h"
68

69

Thomas White's avatar
Thomas White committed
70
/* Write statistics at APPROXIMATELY this interval */
71
72
#define STATS_EVERY_N_SECONDS (5)

73
74
75
76
77
78
enum {
	PEAK_ZAEF,
	PEAK_HDF5,
};


79
/* Information about the indexing process which is common to all patterns */
Thomas White's avatar
Thomas White committed
80
struct index_args
81
82
83
84
85
{
	UnitCell *cell;
	int config_cmfilter;
	int config_noisefilter;
	int config_verbose;
Thomas White's avatar
Thomas White committed
86
	int stream_flags;         /* What goes into the output? */
87
	int config_satcorr;
88
	int config_closer;
89
	int config_insane;
90
	int config_bgsub;
91
	float threshold;
92
	float min_gradient;
93
	float min_snr;
94
	double min_int_snr;
Thomas White's avatar
Thomas White committed
95
	struct detector *det;
Thomas White's avatar
Thomas White committed
96
97
	IndexingMethod *indm;
	IndexingPrivate **ipriv;
Thomas White's avatar
Thomas White committed
98
	int peaks;                /* Peak detection method */
99
	int cellr;
100
	float tols[4];
101
	struct beam_params *beam;
Thomas White's avatar
Thomas White committed
102
	const char *element;
103
	const char *hdf5_peak_path;
104
105
106
	double ir_inn;
	double ir_mid;
	double ir_out;
107

108
109
	/* Output stream */
	FILE *ofh;
110
	const struct copy_hdf5_field *copyme;
Chuck's avatar
Chuck committed
111
	char *outfile;
112
113
114
};


115
/* Information about the indexing process for one pattern */
Thomas White's avatar
Thomas White committed
116
struct pattern_args
117
118
119
120
121
122
123
124
125
{
	/* "Input" */
	char *filename;

	/* "Output" */
	int indexable;
};


Thomas White's avatar
Thomas White committed
126
127
128
129
130
131
static void show_help(const char *s)
{
	printf("Syntax: %s [options]\n\n", s);
	printf(
"Process and index FEL diffraction images.\n"
"\n"
132
" -h, --help               Display this help message.\n"
Thomas White's avatar
Thomas White committed
133
"\n"
134
" -i, --input=<filename>   Specify file containing list of images to process.\n"
Thomas White's avatar
Thomas White committed
135
"                           '-' means stdin, which is the default.\n"
Thomas White's avatar
Thomas White committed
136
137
" -o, --output=<filename>  Write output stream to this file. '-' for stdout.\n"
"                           Default: indexamajig.stream\n"
Thomas White's avatar
Thomas White committed
138
"\n"
Thomas White's avatar
Thomas White committed
139
140
141
142
143
"     --indexing=<methods> Use 'methods' for indexing.  Provide one or more\n"
"                           methods separated by commas.  Choose from:\n"
"                            none     : no indexing (default)\n"
"                            dirax    : invoke DirAx\n"
"                            mosflm   : invoke MOSFLM (DPS)\n"
Thomas White's avatar
Thomas White committed
144
"                            reax     : DPS algorithm with known unit cell\n"
145
" -g. --geometry=<file>    Get detector geometry from file.\n"
146
147
148
" -b, --beam=<file>        Get beam parameters from file (provides nominal\n"
"                           wavelength value if no per-shot value is found in\n"
"                           the HDF5 files.\n"
149
" -p, --pdb=<file>         PDB file from which to get the unit cell to match.\n"
Thomas White's avatar
Thomas White committed
150
"                           Default: 'molecule.pdb'.\n"
151
"     --basename           Remove the directory parts of the filenames.\n"
152
" -x, --prefix=<p>         Prefix filenames from input file with <p>.\n"
153
154
155
"     --peaks=<method>     Use 'method' for finding peaks.  Choose from:\n"
"                           zaef  : Use Zaefferer (2000) gradient detection.\n"
"                                    This is the default method.\n"
156
157
158
"                           hdf5  : Get from a table in HDF5 file.\n"
"     --hdf5-peaks=<p>     Find peaks table in HDF5 file here.\n"
"                           Default: /processing/hitfinder/peakinfo\n"
Thomas White's avatar
Thomas White committed
159
160
"\n\n"
"You can control what information is included in the output stream using\n"
Thomas White's avatar
Thomas White committed
161
"' --record=<flag1>,<flag2>,<flag3>' and so on.  Possible flags are:\n\n"
162
163
" integrated        Include a list of reflection intensities, produced by\n"
"                    integrating around predicted peak locations.\n"
Thomas White's avatar
Thomas White committed
164
"\n"
165
166
" peaks             Include peak locations and intensities from the peak\n"
"                    search.\n"
Thomas White's avatar
Thomas White committed
167
"\n"
168
" peaksifindexed    As 'peaks', but only if the pattern could be indexed.\n"
Thomas White's avatar
Thomas White committed
169
"\n"
170
171
" peaksifnotindexed As 'peaks', but only if the pattern could NOT be indexed.\n"
"\n\n"
Thomas White's avatar
Thomas White committed
172
"The default is '--record=integrated'.\n"
Thomas White's avatar
Thomas White committed
173
174
"\n\n"
"For more control over the process, you might need:\n\n"
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
"  --cell-reduction=<m>  Use <m> as the cell reduction method. Choose from:\n"
"                         none    : no matching, just use the raw cell.\n"
"                         reduce  : full cell reduction.\n"
"                         compare : match by at most changing the order of\n"
"                                   the indices.\n"
"                         compare_ab : compare 'a' and 'b' lengths only.\n"
"    --tolerance=<tol>   Set the tolerances for cell reduction.\n"
"                          Default: 5,5,5,1.5.\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"
"    --no-sat-corr       Don't correct values of saturated peaks using a\n"
"                         table included in the HDF5 file.\n"
"    --threshold=<n>     Only accept peaks above <n> ADU.  Default: 800.\n"
"    --min-gradient=<n>  Minimum gradient for Zaefferer peak search.\n"
"                         Default: 100,000.\n"
"    --min-snr=<n>       Minimum signal-to-noise ratio for peaks.\n"
"                         Default: 5.\n"
"    --min-integration-snr=<n> Minimum signal-to-noise ratio for peaks\n"
"                         during integration. Default: -infinity.\n"
"    --int-radius=<r>    Set the integration radii.  Default: 4,5,7.\n"
"-e, --image=<element>   Use this image from the HDF5 file.\n"
"                          Example: /data/data0.\n"
"                          Default: The first one found.\n"
Thomas White's avatar
Thomas White committed
203
"\n"
204
205
206
207
"\nFor time-resolved stuff, you might want to use:\n\n"
"     --copy-hdf5-field <f>  Copy the value of field <f> into the stream. You\n"
"                             can use this option as many times as you need.\n"
"\n"
208
209
210
"\nOptions for greater performance or verbosity:\n\n"
"     --verbose            Be verbose about indexing.\n"
" -j <n>                   Run <n> analyses in parallel.  Default 1.\n"
211
212
213
"\n"
"\nOptions you probably won't need:\n\n"
"     --no-check-prefix    Don't attempt to correct the --prefix.\n"
Chuck's avatar
Chuck committed
214
215
"     --closer-peak        Don't integrate from the location of a nearby peak\n"
"                           instead of the predicted spot.  Don't use.\n"
216
217
"     --insane             Don't check that the reduced cell accounts for at\n"
"                           least 10%% of the located peaks.\n"
218
219
"     --no-bg-sub          Don't subtract local background estimates from\n"
"                           integrated intensities.\n"
Thomas White's avatar
Thomas White committed
220
);
Thomas White's avatar
Thomas White committed
221
222
223
}


Thomas White's avatar
WIP    
Thomas White committed
224
225
static char *get_pattern(FILE *fh, char **use_this_one_instead,
                         int config_basename, const char *prefix)
Thomas White's avatar
Thomas White committed
226
{
Thomas White's avatar
Thomas White committed
227
	char *line;
Thomas White's avatar
WIP    
Thomas White committed
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
	char *filename;

	/* Get the next filename */
	if ( *use_this_one_instead != NULL ) {

		line = *use_this_one_instead;
		*use_this_one_instead = NULL;

	} else {

		char *rval;

		line = malloc(1024*sizeof(char));
		rval = fgets(line, 1023, fh);
		if ( rval == NULL ) {
			free(line);
			return NULL;
		}
Thomas White's avatar
Thomas White committed
246
247
248

	}

Thomas White's avatar
WIP    
Thomas White committed
249
250
251
252
253
254
255
	chomp(line);

	if ( config_basename ) {
		char *tmp;
		tmp = safe_basename(line);
		free(line);
		line = tmp;
Thomas White's avatar
Thomas White committed
256
	}
Thomas White's avatar
Thomas White committed
257

Thomas White's avatar
WIP    
Thomas White committed
258
259
260
261
262
263
264
	filename = malloc(strlen(prefix)+strlen(line)+1);

	snprintf(filename, 1023, "%s%s", prefix, line);

	free(line);

	return filename;
Chuck's avatar
Chuck committed
265
266
267
}


Thomas White's avatar
Thomas White committed
268
269
static void process_image(const struct index_args *iargs,
                          struct pattern_args *pargs, int cookie)
270
271
272
{
	float *data_for_measurement;
	size_t data_size;
Thomas White's avatar
Thomas White committed
273
274
275
276
277
278
	UnitCell *cell = iargs->cell;
	int config_cmfilter = iargs->config_cmfilter;
	int config_noisefilter = iargs->config_noisefilter;
	int config_verbose = iargs->config_verbose;
	IndexingMethod *indm = iargs->indm;
	struct beam_params *beam = iargs->beam;
Chuck's avatar
Chuck committed
279
280
	int r, check;
	struct hdfile *hdfile;
Thomas White's avatar
Thomas White committed
281
	char *outfile = iargs->outfile;
Chuck's avatar
Chuck committed
282
	struct image image;
Thomas White's avatar
WIP    
Thomas White committed
283
	char *outfilename = iargs->outfile;
Thomas White's avatar
Thomas White committed
284
285
	int fd;
	FILE *fh;
Thomas White's avatar
Thomas White committed
286
	struct flock fl;
Thomas White's avatar
Thomas White committed
287

288
289
	image.features = NULL;
	image.data = NULL;
Thomas White's avatar
Thomas White committed
290
	image.flags = NULL;
291
	image.indexed_cell = NULL;
Thomas White's avatar
Thomas White committed
292
293
	image.det = copy_geom(iargs->det);
	image.copyme = iargs->copyme;
Thomas White's avatar
Thomas White committed
294
	image.reflections = NULL;
Thomas White's avatar
Thomas White committed
295
296
	image.id = cookie;
	image.filename = pargs->filename;
297
298
	image.beam = beam;

Thomas White's avatar
Thomas White committed
299
300
	hdfile = hdfile_open(image.filename);
	if ( hdfile == NULL ) return;
301

Thomas White's avatar
Thomas White committed
302
303
	r = hdfile_set_first_image(hdfile, "/");
	if ( r ) {
Chuck's avatar
Chuck committed
304
305
306
		ERROR("Couldn't select first path\n");
		hdfile_close(hdfile);
		return;
307
308
	}

Chuck's avatar
Chuck committed
309
	check = hdf5_read(hdfile, &image, 1);
Thomas White's avatar
Thomas White committed
310
	if ( check ) {
Chuck's avatar
Chuck committed
311
312
313
		hdfile_close(hdfile);
		return;
	}
314

Thomas White's avatar
Thomas White committed
315
316
317
	if ( (image.width != image.det->max_fs + 1 )
	  || (image.height != image.det->max_ss + 1))
	{
318
		ERROR("Image size doesn't match geometry size"
Chuck's avatar
Chuck committed
319
			" - rejecting image.\n");
320
		ERROR("Image size: %i,%i.  Geometry size: %i,%i\n",
Chuck's avatar
Chuck committed
321
322
		image.width, image.height,
		image.det->max_fs + 1, image.det->max_ss + 1);
323
324
325
326
327
		hdfile_close(hdfile);
		free_detector_geometry(image.det);
		return;
	}

Thomas White's avatar
Thomas White committed
328
329
	if ( image.lambda < 0.0 ) {
		if ( beam != NULL ) {
Thomas White's avatar
Thomas White committed
330
			ERROR("Using nominal photon energy of %.2f eV\n",
Chuck's avatar
Chuck committed
331
			beam->photon_energy);
Thomas White's avatar
Thomas White committed
332
			image.lambda = ph_en_to_lambda(
Chuck's avatar
Chuck committed
333
			eV_to_J(beam->photon_energy));
334
335
		} else {
			ERROR("No wavelength in file, so you need to give "
Chuck's avatar
Chuck committed
336
				"a beam parameters file with -b.\n");
337
			hdfile_close(hdfile);
Thomas White's avatar
Thomas White committed
338
			free_detector_geometry(image.det);
339
340
341
			return;
		}
	}
342
	fill_in_values(image.det, hdfile);
343

Thomas White's avatar
Thomas White committed
344
	if ( config_cmfilter ) {
345
346
347
		filter_cm(&image);
	}

Thomas White's avatar
Thomas White committed
348
349
350
	/* Take snapshot of image after CM subtraction but before
	 * the aggressive noise filter. */
	data_size = image.width * image.height * sizeof(float);
351
352
	data_for_measurement = malloc(data_size);

Thomas White's avatar
Thomas White committed
353
	if ( config_noisefilter ) {
354
355
		filter_noise(&image, data_for_measurement);
	} else {
356
		memcpy(data_for_measurement, image.data, data_size);
357
358
	}

Thomas White's avatar
Thomas White committed
359
360
	switch ( iargs->peaks ) {

Chuck's avatar
Chuck committed
361
362
		case PEAK_HDF5:
		// Get peaks from HDF5
Thomas White's avatar
Thomas White committed
363
364
365
366
367
368
		if (get_peaks(&image, hdfile,
			iargs->hdf5_peak_path)) {
			ERROR("Failed to get peaks from HDF5 file.\n");
		}
		break;

Chuck's avatar
Chuck committed
369
		case PEAK_ZAEF:
Thomas White's avatar
Thomas White committed
370
371
372
373
374
375
376
377
		search_peaks(&image, iargs->threshold,
						iargs->min_gradient,
						iargs->min_snr,
						iargs->ir_inn,
						iargs->ir_mid,
						iargs->ir_out);
		break;

378
	}
Thomas White's avatar
Thomas White committed
379
380
381
382
383
384

	/* 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;

385
	/* Calculate orientation matrix (by magic) */
386
387
388
	image.div = beam->divergence;
	image.bw = beam->bandwidth;
	image.profile_radius = 0.0001e9;
389

Thomas White's avatar
Thomas White committed
390
391
392
	index_pattern(&image, cell, indm, iargs->cellr,
	              config_verbose, iargs->ipriv,
	              iargs->config_insane, iargs->tols);
Thomas White's avatar
Thomas White committed
393

Thomas White's avatar
Thomas White committed
394
	if ( image.indexed_cell != NULL ) {
395
		pargs->indexable = 1;
396
		image.reflections = find_intersections(&image,
Chuck's avatar
Chuck committed
397
398
				image.indexed_cell);
		if (image.reflections != NULL) {
399
			integrate_reflections(&image,
Thomas White's avatar
Thomas White committed
400
401
402
403
404
405
							iargs->config_closer,
							iargs->config_bgsub,
							iargs->min_int_snr,
							iargs->ir_inn,
							iargs->ir_mid,
							iargs->ir_out);
406
		}
407
408
	} else {
		image.reflections = NULL;
Chuck's avatar
Chuck committed
409
	}
410

Chuck's avatar
Chuck committed
411
	/* Write Lock */
Thomas White's avatar
Thomas White committed
412
413
414
415
	fl.l_type = F_WRLCK;
	fl.l_whence = SEEK_SET;
	fl.l_start = 0;
	fl.l_len = 0;  /* Means "lock the whole file" */
Chuck's avatar
Chuck committed
416

Thomas White's avatar
Thomas White committed
417
	fd = open(outfile, O_WRONLY);
Thomas White's avatar
Thomas White committed
418
	if ( fd == -1 ) {
Thomas White's avatar
Thomas White committed
419
		ERROR("Error on opening\n");
Chuck's avatar
Chuck committed
420
421
		exit(1);
	}
Thomas White's avatar
Thomas White committed
422
	if ( fcntl(fd, F_SETLKW, &fl) == -1 ) {
Thomas White's avatar
Thomas White committed
423
		ERROR("Error on setting lock wait\n");
Chuck's avatar
Chuck committed
424
425
426
		exit(1);
	}

Thomas White's avatar
Thomas White committed
427
	fh = fdopen(fd, "a");
Thomas White's avatar
WIP    
Thomas White committed
428
429
	if ( fh == NULL ) {
		ERROR("Couldn't open stream '%s'.\n", outfilename);
Chuck's avatar
Chuck committed
430
	}
Thomas White's avatar
Thomas White committed
431
	write_chunk(fh, &image, hdfile, iargs->stream_flags);
Thomas White's avatar
Thomas White committed
432
	fflush(fh);
Chuck's avatar
Chuck committed
433
434
435

	/* Unlock stream for other processes */
	fl.l_type = F_UNLCK; /* set to unlock same region */
Thomas White's avatar
Thomas White committed
436
	if ( fcntl(fd, F_SETLK, &fl) == -1 ) {
Thomas White's avatar
Thomas White committed
437
		ERROR("fcntl");
Chuck's avatar
Chuck committed
438
		exit(1);
439
	}
Thomas White's avatar
Thomas White committed
440
441

	fclose(fh);  /* close(fd) happens as well because fd was not dup'd */
Thomas White's avatar
Thomas White committed
442

443
	/* Only free cell if found */
444
	cell_free(image.indexed_cell);
445

Thomas White's avatar
Thomas White committed
446
	reflist_free(image.reflections);
447
	free(image.data);
Thomas White's avatar
Thomas White committed
448
	if ( image.flags != NULL ) free(image.flags);
449
450
	image_feature_list_free(image.features);
	hdfile_close(hdfile);
Thomas White's avatar
Thomas White committed
451
	free_detector_geometry(image.det);
452
453
454
}


Thomas White's avatar
Thomas White committed
455
static void run_work(const struct index_args *iargs,
Thomas White's avatar
Thomas White committed
456
                     int filename_pipe, int results_pipe, int cookie)
Thomas White's avatar
Thomas White committed
457
458
{
	int allDone = 0;
Thomas White's avatar
WIP    
Thomas White committed
459
460
461
462
463
464
465
466
467
	FILE *fh;

	fh = fdopen(filename_pipe, "r");
	if ( fh == NULL ) {
		ERROR("Failed to fdopen() the filename pipe!\n");
		close(filename_pipe);
		close(results_pipe);
		return;
	}
Thomas White's avatar
Thomas White committed
468
469
470

	while ( !allDone ) {

Thomas White's avatar
Thomas White committed
471
		struct pattern_args pargs;
Thomas White's avatar
WIP    
Thomas White committed
472
		int  w, c;
Thomas White's avatar
Thomas White committed
473
		char buf[1024];
Thomas White's avatar
WIP    
Thomas White committed
474
475
476
477
478
479
480
		char *line;
		char *rval;

		line = malloc(1024*sizeof(char));
		rval = fgets(line, 1023, fh);
		if ( rval == NULL ) {
			free(line);
481
482
483
484
485
486
487
			if ( feof(fh) ) {
				allDone = 1;
				continue;
			} else {
				ERROR("Read error!\n");
				return;
			}
Thomas White's avatar
WIP    
Thomas White committed
488
		}
Thomas White's avatar
Thomas White committed
489

Thomas White's avatar
WIP    
Thomas White committed
490
491
492
		chomp(line);
		pargs.filename = line;
		pargs.indexable = 0;
Thomas White's avatar
Thomas White committed
493

Thomas White's avatar
WIP    
Thomas White committed
494
		process_image(iargs, &pargs, cookie);
Thomas White's avatar
Thomas White committed
495

Thomas White's avatar
WIP    
Thomas White committed
496
497
498
499
		/* Request another image */
		c = sprintf(buf, "%i\n", pargs.indexable);
		w = write(results_pipe, buf, c);
		if ( w < 0 ) {
500
			ERROR("write P0\n");
Thomas White's avatar
Thomas White committed
501
502
		}

Thomas White's avatar
WIP    
Thomas White committed
503
504
		free(line);

Thomas White's avatar
Thomas White committed
505
506
	}
	/* close my pipes */
Thomas White's avatar
WIP    
Thomas White committed
507
	fclose(fh);
Thomas White's avatar
Thomas White committed
508
509
510
511
	close(results_pipe);
}


512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
#ifdef HAVE_CLOCK_GETTIME

static time_t get_monotonic_seconds()
{
	struct timespec tp;
	clock_gettime(CLOCK_MONOTONIC, &tp);
	return tp.tv_sec;
}

#else

/* Fallback version of the above.  The time according to gettimeofday() is not
 * monotonic, so measuring intervals based on it will screw up if there's a
 * timezone change (e.g. daylight savings) while the program is running. */
static time_t get_monotonic_seconds()
{
	struct timeval tp;
	gettimeofday(&tp, NULL);
	return tp.tv_sec;
}

#endif

535

536
537
538
static int parse_cell_reduction(const char *scellr, int *err,
                                int *reduction_needs_cell)
{
Thomas White's avatar
Thomas White committed
539
	*err = 0;
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
	if ( strcmp(scellr, "none") == 0 ) {
		*reduction_needs_cell = 0;
		return CELLR_NONE;
	} else if ( strcmp(scellr, "reduce") == 0) {
		*reduction_needs_cell = 1;
		return CELLR_REDUCE;
	} else if ( strcmp(scellr, "compare") == 0) {
		*reduction_needs_cell = 1;
		return CELLR_COMPARE;
	} else if ( strcmp(scellr, "compare_ab") == 0) {
		*reduction_needs_cell = 1;
		return CELLR_COMPARE_AB;
	} else {
		*err = 1;
		*reduction_needs_cell = 0;
		return CELLR_NONE;
	}
}


Thomas White's avatar
Thomas White committed
560
561
562
563
int main(int argc, char *argv[])
{
	int c;
	char *filename = NULL;
564
	char *outfile = NULL;
Thomas White's avatar
Thomas White committed
565
	FILE *fh;
566
	FILE *ofh;
567
	char *rval = NULL;
568
	int config_noindex = 0;
569
570
	int config_cmfilter = 0;
	int config_noisefilter = 0;
571
	int config_verbose = 0;
572
	int config_satcorr = 1;
573
	int config_checkprefix = 1;
Chuck's avatar
Chuck committed
574
	int config_closer = 0;
575
	int config_insane = 0;
576
	int config_bgsub = 1;
577
	int config_basename = 0;
578
	float threshold = 800.0;
579
	float min_gradient = 100000.0;
Thomas White's avatar
Thomas White committed
580
	float min_snr = 5.0;
581
	double min_int_snr = -INFINITY;
Thomas White's avatar
Thomas White committed
582
583
	struct detector *det;
	char *geometry = NULL;
Thomas White's avatar
Thomas White committed
584
585
586
587
	IndexingMethod *indm;
	IndexingPrivate **ipriv;
	int indexer_needs_cell;
	int reduction_needs_cell;
Thomas White's avatar
Thomas White committed
588
	char *indm_str = NULL;
589
	UnitCell *cell;
Thomas White's avatar
Thomas White committed
590
	char *pdb = NULL;
591
	char *prefix = NULL;
592
	char *speaks = NULL;
593
	char *scellr = NULL;
594
	char *toler = NULL;
Thomas White's avatar
Thomas White committed
595
	float tols[4] = {5.0, 5.0, 5.0, 1.5}; /* a,b,c,angles (%,%,%,deg) */
596
	int cellr;
597
	int peaks;
Thomas White's avatar
Thomas White committed
598
	int n_proc = 1;
599
	char *prepare_line;
Thomas White's avatar
Thomas White committed
600
	char prepare_filename[1024];
Thomas White's avatar
WIP    
Thomas White committed
601
	char *use_this_one_instead;
Thomas White's avatar
Thomas White committed
602
	struct index_args iargs;
603
	struct beam_params *beam = NULL;
Thomas White's avatar
Thomas White committed
604
	char *element = NULL;
605
	double nominal_photon_energy;
Thomas White's avatar
Thomas White committed
606
	int stream_flags = STREAM_INTEGRATED;
607
	char *hdf5_peak_path = NULL;
608
	struct copy_hdf5_field *copyme;
609
610
611
612
	char *intrad = NULL;
	float ir_inn = 4.0;
	float ir_mid = 5.0;
	float ir_out = 7.0;
Thomas White's avatar
Thomas White committed
613
614
615
616
617
	int n_indexable, n_processed, n_indexable_last_stats;
	int n_processed_last_stats;
	int t_last_stats;
	pid_t *pids;
	int *filename_pipes;
618
	FILE **result_fhs;
Thomas White's avatar
Thomas White committed
619
620
	fd_set fds;
	int i;
621
622
	int allDone;
	int *finished;
623
624
625
626
627
628

	copyme = new_copy_hdf5_field_list();
	if ( copyme == NULL ) {
		ERROR("Couldn't allocate HDF5 field list.\n");
		return 1;
	}
Thomas White's avatar
Thomas White committed
629
630
631
632
633

	/* Long options */
	const struct option longopts[] = {
		{"help",               0, NULL,               'h'},
		{"input",              1, NULL,               'i'},
634
		{"output",             1, NULL,               'o'},
635
		{"no-index",           0, &config_noindex,     1},
Thomas White's avatar
Thomas White committed
636
		{"indexing",           1, NULL,               'z'},
Thomas White's avatar
Thomas White committed
637
		{"geometry",           1, NULL,               'g'},
638
		{"beam",               1, NULL,               'b'},
639
640
		{"filter-cm",          0, &config_cmfilter,    1},
		{"filter-noise",       0, &config_noisefilter, 1},
641
		{"verbose",            0, &config_verbose,     1},
Thomas White's avatar
Thomas White committed
642
		{"pdb",                1, NULL,               'p'},
643
		{"prefix",             1, NULL,               'x'},
644
645
		{"no-sat-corr",        0, &config_satcorr,     0},
		{"sat-corr",           0, &config_satcorr,     1}, /* Compat */
646
		{"threshold",          1, NULL,               't'},
647
		{"no-check-prefix",    0, &config_checkprefix, 0},
648
		{"no-closer-peak",     0, &config_closer,      0},
Chuck's avatar
Chuck committed
649
		{"closer-peak",        0, &config_closer,      1},
650
		{"insane",             0, &config_insane,      1},
Thomas White's avatar
Thomas White committed
651
		{"image",              1, NULL,               'e'},
652
		{"basename",           0, &config_basename,    1},
Thomas White's avatar
Thomas White committed
653
654
655
656
657
658
		{"bg-sub",             0, &config_bgsub,       1}, /* Compat */
		{"no-bg-sub",          0, &config_bgsub,       0},

		{"peaks",              1, NULL,                2},
		{"cell-reduction",     1, NULL,                3},
		{"min-gradient",       1, NULL,                4},
Thomas White's avatar
Thomas White committed
659
		{"record",             1, NULL,                5},
660
661
		{"cpus",               1, NULL,                6},
		{"cpugroup",           1, NULL,                7},
662
		{"cpuoffset",          1, NULL,                8},
663
		{"hdf5-peaks",         1, NULL,                9},
664
		{"copy-hdf5-field",    1, NULL,               10},
Thomas White's avatar
Thomas White committed
665
666
667
		{"min-snr",            1, NULL,               11},
		{"min-integration-snr",1, NULL,               12},
		{"tolerance",          1, NULL,               13},
668
		{"int-radius",         1, NULL,               14},
Thomas White's avatar
Thomas White committed
669
670
671
672
		{0, 0, NULL, 0}
	};

	/* Short options */
Chuck's avatar
Chuck committed
673
	while ((c = getopt_long(argc, argv, "hi:o:z:p:x:j:g:t:b:e:",
Thomas White's avatar
Thomas White committed
674
675
	                        longopts, NULL)) != -1)
	{
Thomas White's avatar
Thomas White committed
676
		switch (c) {
Thomas White's avatar
Thomas White committed
677
678

			case 'h' :
Thomas White's avatar
Thomas White committed
679
680
681
			show_help(argv[0]);
			return 0;

Thomas White's avatar
Thomas White committed
682
			case 'i' :
Thomas White's avatar
Thomas White committed
683
684
685
			filename = strdup(optarg);
			break;

Thomas White's avatar
Thomas White committed
686
			case 'o' :
687
688
689
			outfile = strdup(optarg);
			break;

Thomas White's avatar
Thomas White committed
690
			case 'z' :
Thomas White's avatar
Thomas White committed
691
692
693
			indm_str = strdup(optarg);
			break;

Thomas White's avatar
Thomas White committed
694
			case 'p' :
Thomas White's avatar
Thomas White committed
695
696
697
			pdb = strdup(optarg);
			break;

Thomas White's avatar
Thomas White committed
698
			case 'x' :
699
700
701
			prefix = strdup(optarg);
			break;

Thomas White's avatar
Thomas White committed
702
			case 'j' :
Thomas White's avatar
Thomas White committed
703
			n_proc = atoi(optarg);
704
705
			break;

Thomas White's avatar
Thomas White committed
706
			case 'g' :
Thomas White's avatar
Thomas White committed
707
708
709
			geometry = strdup(optarg);
			break;

Thomas White's avatar
Thomas White committed
710
			case 't' :
711
712
713
			threshold = strtof(optarg, NULL);
			break;

Thomas White's avatar
Thomas White committed
714
			case 'b' :
715
716
717
718
719
720
721
722
			beam = get_beam_parameters(optarg);
			if ( beam == NULL ) {
				ERROR("Failed to load beam parameters"
				      " from '%s'\n", optarg);
				return 1;
			}
			break;

Thomas White's avatar
Thomas White committed
723
724
			case 'e' :
			element = strdup(optarg);
725
726
			break;

Thomas White's avatar
Thomas White committed
727
728
			case 2 :
			speaks = strdup(optarg);
729
730
			break;

Thomas White's avatar
Thomas White committed
731
732
			case 3 :
			scellr = strdup(optarg);
733
734
			break;

Thomas White's avatar
Thomas White committed
735
			case 4 :
736
737
738
			min_gradient = strtof(optarg, NULL);
			break;

Thomas White's avatar
Thomas White committed
739
			case 5 :
Thomas White's avatar
Thomas White committed
740
741
742
743
			stream_flags = parse_stream_flags(optarg);
			if ( stream_flags < 0 ) return 1;
			break;

Thomas White's avatar
Thomas White committed
744
745
746
			case 6 :
			case 7 :
			case 8 :
747
748
			ERROR("The options --cpus, --cpugroup and --cpuoffset"
			      " are no longer used by indexamajig.\n");
749
750
			break;

Thomas White's avatar
Thomas White committed
751
			case 9 :
752
753
754
			hdf5_peak_path = strdup(optarg);
			break;

Thomas White's avatar
Thomas White committed
755
			case 10 :
756
757
758
			add_copy_hdf5_field(copyme, optarg);
			break;

Thomas White's avatar
Thomas White committed
759
760
761
762
763
764
			case 11 :
			min_snr = strtof(optarg, NULL);
			break;

			case 12 :
			min_int_snr = strtof(optarg, NULL);
Thomas White's avatar
Thomas White committed
765
766
			break;

Thomas White's avatar
Thomas White committed
767
768
769
770
			case 13 :
			toler = strdup(optarg);
			break;

771
772
773
774
			case 14 :
			intrad = strdup(optarg);
			break;

Thomas White's avatar
Thomas White committed
775
776
777
778
			case 0 :
			break;

			default :
Thomas White's avatar
Thomas White committed
779
			return 1;
Thomas White's avatar
Thomas White committed
780

Thomas White's avatar
Thomas White committed
781
782
783
784
785
786
787
788
789
790
791
792
793
		}

	}

	if ( filename == NULL ) {
		filename = strdup("-");
	}
	if ( strcmp(filename, "-") == 0 ) {
		fh = stdin;
	} else {
		fh = fopen(filename, "r");
	}
	if ( fh == NULL ) {
Thomas White's avatar
Thomas White committed
794
		ERROR("Failed to open input file '%s'\n", filename);
Thomas White's avatar
Thomas White committed
795
796
		return 1;
	}
Thomas White's avatar
Thomas White committed
797
	free(filename);
Thomas White's avatar
Thomas White committed
798

799
800
801
802
803
804
805
806
807
808
809
810
811
	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;
	}

812
813
814
815
	if ( hdf5_peak_path == NULL ) {
		hdf5_peak_path = strdup("/processing/hitfinder/peakinfo");
	}

816
817
818
819
820
821
822
823
824
825
826
827
828
	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;
	}
Thomas White's avatar
Thomas White committed
829
	free(speaks);
830

Thomas White's avatar
Thomas White committed
831
832
833
834
	if ( pdb == NULL ) {
		pdb = strdup("molecule.pdb");
	}

835
	if ( prefix == NULL ) {
Thomas White's avatar
Thomas White committed
836
		prefix = strdup("");
837
	} else {
838
839
840
		if ( config_checkprefix ) {
			prefix = check_prefix(prefix);
		}
841
842
	}

Thomas White's avatar
Thomas White committed
843
	if ( n_proc == 0 ) {
Chuck's avatar
Chuck committed
844
		ERROR("Invalid number of processes.\n");
845
846
847
		return 1;
	}

848
849
	if ( (indm_str == NULL) ||
	     ((indm_str != NULL) && (strcmp(indm_str, "none") == 0)) ) {
850
		STATUS("Not indexing anything.\n");
851
		indexer_needs_cell = 0;
852
853
854
		reduction_needs_cell = 0;
		indm = NULL;
		cellr = CELLR_NONE;
Thomas White's avatar
Thomas White committed
855
	} else {
856
857
858
859
860
861
862
863
		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=<method>.\n");
			indm = NULL;
			indexer_needs_cell = 0;
		} else {
Thomas White's avatar
Thomas White committed
864
865
			indm = build_indexer_list(indm_str,
			                          &indexer_needs_cell);
866
867
868
869
870
			if ( indm == NULL ) {
				ERROR("Invalid indexer list '%s'\n", indm_str);
				return 1;
			}
			free(indm_str);
Thomas White's avatar
Thomas White committed
871
		}
Thomas White's avatar
Thomas White committed
872

873
874
875
876
877
878
879
		reduction_needs_cell = 0;
		if ( scellr == NULL ) {
			STATUS("You didn't specify a cell reduction method, so"
			       " I'm going to use 'reduce'.\n");
			cellr = CELLR_REDUCE;
			reduction_needs_cell = 1;
		} else {
880
881
882
883
884
885
886
887
888
			int err;
			cellr = parse_cell_reduction(scellr, &err,
			                             &reduction_needs_cell);
			if ( err ) {
				ERROR("Unrecognised cell reduction '%s'\n",
			              scellr);
				return 1;
			}
			free(scellr);
889
		}
890
891
	}

892
893
894
	/* No indexing -> no reduction */
	if ( indm == NULL ) reduction_needs_cell = 0;

895
896
	if ( toler != NULL ) {
		int ttt;
Thomas White's avatar
Thomas White committed
897
898
		ttt = sscanf(toler, "%f,%f,%f,%f",
		             &tols[0], &tols[1], &tols[2], &tols[3] );
899
900
901
902
903
904
		if ( ttt != 4 ) {
			ERROR("Invalid parameters for '--tolerance'\n");
			return 1;
		}
	}

905
906
907
908
909
910
911
912
913
914
915
916
917
	if ( intrad != NULL ) {
		int r;
		r = sscanf(intrad, "%f,%f,%f", &ir_inn, &ir_mid, &ir_out);
		if ( r != 3 ) {
			ERROR("Invalid parameters for '--int-radius'\n");
			return 1;
		}
	} else {
		STATUS("WARNING: You did not specify --int-radius.\n");
		STATUS("WARNING: I will use the default values, which are"
		       " probably not appropriate for your patterns.\n");
	}

Thomas White's avatar
Thomas White committed
918
919
920
921
922
923
924
925
926
927
928
929
	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);

Thomas White's avatar
Thomas White committed
930
	if ( reduction_needs_cell || indexer_needs_cell ) {
931
932
		cell = load_cell_from_pdb(pdb);
		if ( cell == NULL ) {
933
934
935
			ERROR("Couldn't read unit cell (from %s)\n", pdb);
			return 1;
		}
936
	} else {
Thomas White's avatar
Thomas White committed
937
938
		STATUS("No cell needed for these choices of indexing"
		       " and reduction.\n");
939
		cell = NULL;
940
	}
941
	free(pdb);
942

Thomas White's avatar
Thomas White committed
943
	write_stream_header(ofh, argc, argv);
944

945
946
947
948
949
	if ( beam != NULL ) {
		nominal_photon_energy = beam->photon_energy;
	} else {
		STATUS("No beam parameters file was given, so I'm taking the"
		       " nominal photon energy to be 2 keV.\n");
Thomas White's avatar
Thomas White committed
950
951
952
		ERROR("I'm also going to assume 1 ADU per photon, which is");
		ERROR(" almost certainly wrong.  Peak sigmas will be"
		      " incorrect.\n");
953
954
955
		nominal_photon_energy = 2000.0;
	}

956
	/* Get first filename and use it to set up the indexing */
Thomas White's avatar
Thomas White committed
957
958
	prepare_line = malloc(1024);
	rval = fgets(prepare_line, 1023, fh);
959
960
961
	if ( rval == NULL ) {
		ERROR("Failed to get filename to prepare indexing.\n");
		return 1;
962
	}
Thomas White's avatar
WIP    
Thomas White committed
963
	use_this_one_instead = strdup(prepare_line);
964
	chomp(prepare_line);
965
966
967
968
969
970
	if ( config_basename ) {
		char *tmp;
		tmp = safe_basename(prepare_line);
		free(prepare_line);
		prepare_line = tmp;
	}
Thomas White's avatar
Thomas White committed
971
	snprintf(prepare_filename, 1023, "%s%s", prefix, prepare_line);
972
973

	/* Prepare the indexer */
Thomas White's avatar
Thomas White committed
974
975
976
977
978
979
980
981
982
	if ( indm != NULL ) {
		ipriv = prepare_indexing(indm, cell, prepare_filename, det,
		                         nominal_photon_energy);
		if ( ipriv == NULL ) {
			ERROR("Failed to prepare indexing.\n");
			return 1;
		}
	} else {
		ipriv = NULL;
983
984
	}

Thomas White's avatar
Thomas White committed
985
	gsl_set_error_handler_off();
Thomas White's avatar
Thomas White committed
986

Thomas White's avatar
Thomas White committed
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
	/* Static worker args */
	iargs.cell = cell;
	iargs.config_cmfilter = config_cmfilter;
	iargs.config_noisefilter = config_noisefilter;
	iargs.config_verbose = config_verbose;
	iargs.config_satcorr = config_satcorr;
	iargs.config_closer = config_closer;
	iargs.config_insane = config_insane;
	iargs.config_bgsub = config_bgsub;
	iargs.cellr = cellr;
	iargs.tols[0] = tols[0];
	iargs.tols[1] = tols[1];
	iargs.tols[2] = tols[2];
	iargs.tols[3] = tols[3];