pattern_sim.c 16.5 KB
Newer Older
1
/*
Thomas White's avatar
Thomas White committed
2
3
4
 * pattern_sim.c
 *
 * Simulate diffraction patterns from small crystals
5
 *
Thomas White's avatar
Thomas White committed
6
 * (c) 2006-2010 Thomas White <taw@physics.org>
7
 *
Thomas White's avatar
Thomas White committed
8
 * Part of CrystFEL - crystallography with a FEL
9
10
11
12
13
14
15
16
17
18
19
20
21
 *
 */


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

#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
Thomas White's avatar
Thomas White committed
22
#include <getopt.h>
23

24
#include "image.h"
Thomas White's avatar
Thomas White committed
25
#include "diffraction.h"
26
#include "diffraction-gpu.h"
27
#include "cell.h"
28
29
#include "utils.h"
#include "hdf5-file.h"
Thomas White's avatar
Thomas White committed
30
#include "detector.h"
31
#include "peaks.h"
Thomas White's avatar
Thomas White committed
32
#include "sfac.h"
33
#include "reflections.h"
34
#include "beam-parameters.h"
35
36


Thomas White's avatar
Thomas White committed
37
static void show_help(const char *s)
38
{
39
	printf("Syntax: %s [options]\n\n", s);
Thomas White's avatar
Thomas White committed
40
	printf(
Thomas White's avatar
Thomas White committed
41
"Simulate diffraction patterns from small crystals probed with femtosecond\n"
Thomas White's avatar
Thomas White committed
42
43
"pulses of X-rays from a free electron laser.\n"
"\n"
44
" -h, --help                Display this help message.\n"
Thomas White's avatar
Thomas White committed
45
"\n"
46
47
48
" -p, --pdb=<file>          PDB file from which to get the unit cell.\n"
"                            (The actual Bragg intensities come from the\n"
"                            intensities file)\n"
49
"     --simulation-details  Show technical details of the simulation.\n"
50
"     --gpu                 Use the GPU to speed up the calculation.\n"
Thomas White's avatar
Thomas White committed
51
52
"     --gpu-dev=<n>         Use GPU device <n>.  Omit this option to see the\n"
"                            available devices.\n"
53
54
" -g, --geometry=<file>     Get detector geometry from file.\n"
" -b, --beam=<file>         Get beam parameters from file.\n"
55
56
57
58
"\n"
"     --near-bragg          Output h,k,l,I near Bragg conditions.\n"
" -n, --number=<N>          Generate N images.  Default 1.\n"
"     --no-images           Do not output any HDF5 files.\n"
59
60
61
62
63
64
" -o, --output=<filename>   Output HDF5 filename.  Default: sim.h5.\n"
"                            If you choose to simulate more than one pattern,\n"
"                            the filename given will be postfixed with a\n"
"                            hyphen, the image number and '.h5'.  In this\n"
"                            case, the default value is 'sim', such that the\n"
"                            files produced are sim-1.h5, sim-2.h5 and so on.\n"
Thomas White's avatar
Thomas White committed
65
" -r, --random-orientation  Use a randomly generated orientation\n"
66
"                            (a new orientation will be used for each image).\n"
67
68
"     --powder=<file>       Write a summed pattern of all images simulated by\n"
"                            this invocation as the given filename.\n"
69
" -i, --intensities=<file>  Specify file containing reflection intensities\n"
Thomas White's avatar
Thomas White committed
70
"                            (and phases) to use.\n"
71
" -y, --symmetry=<sym>      The symmetry of the intensities file.\n"
Thomas White's avatar
Thomas White committed
72
" -t, --gradients=<method>  Use <method> for the calculation of shape\n"
73
74
"                            transform intensities.  Choose from:\n"
"                             mosaic      : Take the intensity of the nearest\n"
Thomas White's avatar
Thomas White committed
75
76
77
"                                           Bragg position.  This is the\n"
"                                           fastest method and the only one\n"
"                                           supported on the GPU.\n"
78
79
"                             interpolate : Interpolate trilinearly between\n"
"                                           six adjacent Bragg intensities.\n"
Thomas White's avatar
Thomas White committed
80
81
"                                           This method has intermediate\n"
"                                           accuracy.\n"
82
"                             phased      : As 'interpolate', but take phase\n"
Thomas White's avatar
Thomas White committed
83
84
85
"                                           values into account.  This is the\n"
"                                           most accurate method, but the\n"
"                                           slowest.\n"
86
87
88
"     --really-random       Use really random numbers for the orientation and\n"
"                            crystal size.  By default, the same sequence\n"
"                            will be used for each new run.\n"
89
90
91
92
93
"     --min-size=<s>        Generate random crystal sizes using <s> as the\n"
"                            minimum crystal size in nm. --max-size is also\n"
"                            required.\n"
"     --max-size=<s>        Use <s> as the maximum crystal size in nm.\n"
"                            --min-size is also required.\n"
Thomas White's avatar
Thomas White committed
94
95
96
97
98
"\n"
"By default, the simulation aims to be as accurate as possible.  For greater\n"
"speed, or for testing, you can choose to disable certain things using the\n"
"following options.\n"
"\n"
99
"     --no-water            Do not simulate water background.\n"
Thomas White's avatar
Thomas White committed
100
"     --no-noise            Do not calculate Poisson noise.\n"
101
);
Thomas White's avatar
Thomas White committed
102
103
104
}


105
static void show_details()
Thomas White's avatar
Thomas White committed
106
{
Thomas White's avatar
Thomas White committed
107
108
109
110
111
	printf(
"This program simulates diffraction patterns from small crystals illuminated\n"
"with femtosecond X-ray pulses from a free electron laser.\n"
"\n"
"The lattice transform from the specified number of unit cells is calculated\n"
112
113
"using the closed-form solution for a truncated lattice faceted on the\n"
"(001), (010) and (100) planes:\n"
Thomas White's avatar
Thomas White committed
114
"\n"
115
116
117
"I_latt(q) =  sin^2(pi*na*q.a)/sin^2(pi*q.a)\n"
"           * sin^2(pi*nb*q.b)/sin^2(pi*q.b)\n"
"           * sin^2(pi*nc*q.c)/sin^2(pi*q.c)\n"
Thomas White's avatar
Thomas White committed
118
119
120
121
"\n"
"na = number of unit cells in 'a' direction (likewise nb, nc)\n"
" q = reciprocal vector (1/d convention, not 2pi/d)\n"
"\n"
122
123
124
"This is multiplied by a model of the underlying molecular transform, I_mol(q).\n"
"This can be approximated to varying levels of accuracy by the methods given by\n"
"the '--gradients' option.\n"
Thomas White's avatar
Thomas White committed
125
"\n"
126
127
"Intensity from water is added according to the first term of equation 5\n"
"from Phys Chem Chem Phys 2003 (5) 1981--1991.  This simulates the\n"
128
"coherent, elastic part of the diffuse scattering from the water jet only.\n"
Thomas White's avatar
Thomas White committed
129
130
131
"\n"
"Expected intensities at the CCD are then calculated using:\n"
"\n"
132
"I(q) = I0 * r^2 * I_latt(q) * I_mol(q) * S\n"
Thomas White's avatar
Thomas White committed
133
134
135
136
"\n"
"I0 = number of photons per unit area in the incident beam\n"
" r = Thomson radius\n"
" S = solid angle of corresponding pixel\n"
137
138
139
"\n"
"Polarisation is not currently included in pattern_sim, although it is included\n"
"in the analysis of Bragg peaks by 'indexamajig'.\n"
Thomas White's avatar
Thomas White committed
140
141
"\n"
"Poisson counts are generated from the expected intensities using Knuth's\n"
Thomas White's avatar
Thomas White committed
142
143
144
"algorithm.  When the intensity is sufficiently high that Knuth's algorithm\n"
"would result in machine precision problems, a normal distribution with\n"
"standard deviation sqrt(I) is used instead.\n"
145
);
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
}


static struct quaternion read_quaternion()
{
	do {

		int r;
		float w, x, y, z;
		char line[1024];
		char *rval;

		printf("Please input quaternion: w x y z\n");
		rval = fgets(line, 1023, stdin);
		if ( rval == NULL ) return invalid_quaternion();
		chomp(line);

		r = sscanf(line, "%f %f %f %f", &w, &x, &y, &z);
		if ( r == 4 ) {

			struct quaternion quat;

			quat.w = w;
			quat.x = x;
			quat.y = y;
			quat.z = z;

			return quat;

		} else {
176
			ERROR("Invalid rotation '%s'\n", line);
177
178
179
		}

	} while ( 1 );
180
181
182
}


183
184
185
186
187
188
189
190
191
192
193
194
static int random_ncells(double len, double min_nm, double max_nm)
{
	int min_cells, max_cells, wid;

	min_cells = min_nm*1e-9 / len;
	max_cells = max_nm*1e-9 / len;
	wid = max_cells - min_cells;

	return wid*random()/RAND_MAX + min_cells;
}


195
196
int main(int argc, char *argv[])
{
197
	int c;
198
	struct image image;
199
	struct gpu_context *gctx = NULL;
200
	double *powder;
201
202
	char *intfile = NULL;
	double *intensities;
203
	char *rval;
204
	double *phases;
205
	unsigned char *flags;
Thomas White's avatar
Thomas White committed
206
	int config_simdetails = 0;
207
208
	int config_nearbragg = 0;
	int config_randomquat = 0;
209
	int config_noimages = 0;
210
	int config_nowater = 0;
Thomas White's avatar
Thomas White committed
211
	int config_nonoise = 0;
212
	int config_nosfac = 0;
213
	int config_gpu = 0;
214
	int config_random = 0;
215
	char *powder_fn = NULL;
216
	char *filename = NULL;
217
	char *grad_str = NULL;
218
	char *outfile = NULL;
Thomas White's avatar
Thomas White committed
219
	char *geometry = NULL;
220
	char *beamfile = NULL;
221
	GradientMethod grad;
Thomas White's avatar
Thomas White committed
222
223
224
	int ndone = 0;    /* Number of simulations done (images or not) */
	int number = 1;   /* Number used for filename of image */
	int n_images = 1; /* Generate one image by default */
225
	int done = 0;
Thomas White's avatar
Thomas White committed
226
227
	UnitCell *input_cell;
	struct quaternion orientation;
Thomas White's avatar
Thomas White committed
228
	int gpu_dev = -1;
229
230
231
	int random_size = 0;
	double min_size = 0.0;
	double max_size = 0.0;
232
	char *sym = NULL;
Thomas White's avatar
Thomas White committed
233

234
	/* Long options */
Thomas White's avatar
Thomas White committed
235
	const struct option longopts[] = {
236
237
		{"help",               0, NULL,               'h'},
		{"simulation-details", 0, &config_simdetails,  1},
238
		{"gpu",                0, &config_gpu,         1},
239
240
241
		{"near-bragg",         0, &config_nearbragg,   1},
		{"random-orientation", 0, NULL,               'r'},
		{"number",             1, NULL,               'n'},
Thomas White's avatar
Thomas White committed
242
		{"no-images",          0, &config_noimages,    1},
243
		{"no-water",           0, &config_nowater,     1},
Thomas White's avatar
Thomas White committed
244
		{"no-noise",           0, &config_nonoise,     1},
245
		{"intensities",        1, NULL,               'i'},
246
		{"symmetry",           1, NULL,               'y'},
247
		{"powder",             1, NULL,               'w'},
Thomas White's avatar
Thomas White committed
248
		{"gradients",          1, NULL,               't'},
249
		{"pdb",                1, NULL,               'p'},
250
		{"output",             1, NULL,               'o'},
Thomas White's avatar
Thomas White committed
251
		{"geometry",           1, NULL,               'g'},
252
		{"beam",               1, NULL,               'b'},
253
		{"really-random",      0, &config_random,      1},
Thomas White's avatar
Thomas White committed
254
		{"gpu-dev",            1, NULL,                2},
255
256
		{"min-size",           1, NULL,                3},
		{"max-size",           1, NULL,                4},
257
		{0, 0, NULL, 0}
Thomas White's avatar
Thomas White committed
258
	};
259

260
	/* Short options */
261
	while ((c = getopt_long(argc, argv, "hrn:i:t:p:o:g:b:y:",
Thomas White's avatar
Thomas White committed
262
	                        longopts, NULL)) != -1) {
263

Thomas White's avatar
Thomas White committed
264
		switch (c) {
Thomas White's avatar
Thomas White committed
265
		case 'h' :
Thomas White's avatar
Thomas White committed
266
267
			show_help(argv[0]);
			return 0;
268

Thomas White's avatar
Thomas White committed
269
		case 'r' :
270
271
272
			config_randomquat = 1;
			break;

Thomas White's avatar
Thomas White committed
273
		case 'n' :
274
275
276
277
278
			n_images = strtol(optarg, &rval, 10);
			if ( *rval != '\0' ) {
				ERROR("Invalid number of images.\n");
				return 1;
			}
279
280
			break;

Thomas White's avatar
Thomas White committed
281
		case 'i' :
282
283
284
			intfile = strdup(optarg);
			break;

Thomas White's avatar
Thomas White committed
285
		case 't' :
286
287
288
			grad_str = strdup(optarg);
			break;

Thomas White's avatar
Thomas White committed
289
		case 'p' :
290
291
292
			filename = strdup(optarg);
			break;

Thomas White's avatar
Thomas White committed
293
		case 'o' :
294
295
296
			outfile = strdup(optarg);
			break;

Thomas White's avatar
Thomas White committed
297
		case 'w' :
298
299
300
			powder_fn = strdup(optarg);
			break;

Thomas White's avatar
Thomas White committed
301
		case 'g' :
Thomas White's avatar
Thomas White committed
302
303
304
			geometry = strdup(optarg);
			break;

305
306
307
308
		case 'b' :
			beamfile = strdup(optarg);
			break;

309
310
311
312
		case 'y' :
			sym = strdup(optarg);
			break;

Thomas White's avatar
Thomas White committed
313
314
315
316
		case 2 :
			gpu_dev = atoi(optarg);
			break;

317
		case 3 :
318
319
320
321
322
			min_size = strtod(optarg, &rval);
			if ( *rval != '\0' ) {
				ERROR("Invalid minimum size.\n");
				return 1;
			}
323
324
325
326
			random_size++;
			break;

		case 4 :
327
328
329
330
331
			max_size = strtod(optarg, &rval);
			if ( *rval != '\0' ) {
				ERROR("Invalid maximum size.\n");
				return 1;
			}
332
333
334
			random_size++;
			break;

Thomas White's avatar
Thomas White committed
335
		case 0 :
Thomas White's avatar
Thomas White committed
336
			break;
337

Thomas White's avatar
Thomas White committed
338
		default :
Thomas White's avatar
Thomas White committed
339
340
			return 1;
		}
341
342
343

	}

344
	if ( config_random ) {
345
346
347
348
349
350
351
352
		FILE *fh;
		unsigned int seed;
		fh = fopen("/dev/urandom", "r");
		fread(&seed, sizeof(seed), 1, fh);
		fclose(fh);
		srand(seed);
	}

353
354
355
356
357
	if ( random_size == 1 ) {
		ERROR("You must specify both --min-size and --max-size.\n");
		return 1;
	}

358
359
360
361
	if ( filename == NULL ) {
		filename = strdup("molecule.pdb");
	}

362
363
364
365
366
367
368
369
	if ( outfile == NULL ) {
		if ( n_images == 1 ) {
			outfile = strdup("sim.h5");
		} else {
			outfile = strdup("sim");
		}
	}

370
371
	if ( sym == NULL ) sym = strdup("1");

Thomas White's avatar
Thomas White committed
372
	if ( config_simdetails ) {
373
		show_details();
Thomas White's avatar
Thomas White committed
374
375
376
		return 0;
	}

377
378
379
380
381
382
	if ( (!config_nowater) && config_gpu ) {
		ERROR("Cannot simulate water scattering on the GPU.\n");
		ERROR("Please try again with the --no-water option.\n");
		return 1;
	}

383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
	if ( grad_str == NULL ) {
		STATUS("You didn't specify a gradient calculation method, so"
		       " I'm using the 'mosaic' method, which is fastest.\n");
		grad = GRADIENT_MOSAIC;
	} else if ( strcmp(grad_str, "mosaic") == 0 ) {
		grad = GRADIENT_MOSAIC;
	} else if ( strcmp(grad_str, "interpolate") == 0) {
		grad = GRADIENT_INTERPOLATE;
	} else if ( strcmp(grad_str, "phased") == 0) {
		grad = GRADIENT_PHASED;
	} else {
		ERROR("Unrecognised gradient method '%s'\n", grad_str);
		return 1;
	}
	free(grad_str);

	if ( config_gpu && (grad != GRADIENT_MOSAIC) ) {
		ERROR("Only the mosaic method can be used for gradients when"
		      "calculating on the GPU.\n");
		return 1;
	}

Thomas White's avatar
Thomas White committed
405
406
407
408
409
	if ( geometry == NULL ) {
		ERROR("You need to specify a geometry file with --geometry\n");
		return 1;
	}

410
411
412
413
414
415
	if ( beamfile == NULL ) {
		ERROR("You need to specify a beam parameter file"
		      " with --beam\n");
		return 1;
	}

416
	if ( intfile == NULL ) {
417

418
419
420
421
422
423
		/* Gentle reminder */
		STATUS("You didn't specify the file containing the ");
		STATUS("reflection intensities (with --intensities).\n");
		STATUS("I'll simulate a flat intensity distribution.\n");
		intensities = NULL;
		phases = NULL;
424
425
		flags = NULL;

426
	} else {
427
428

		int i;
Thomas White's avatar
Thomas White committed
429
		ReflItemList *items;
430

431
432
433
434
435
		if ( grad == GRADIENT_PHASED ) {
			phases = new_list_phase();
		} else {
			phases = NULL;
		}
Thomas White's avatar
Thomas White committed
436
437
		intensities = new_list_intensity();
		phases = new_list_phase();
438
		flags = new_list_flag();
439
440
		items = read_reflections(intfile, intensities, phases,
		                         NULL, NULL);
441
		free(intfile);
442
443
444
445
446
447

		for ( i=0; i<num_items(items); i++ ) {
			struct refl_item *it = get_item(items, i);
			set_flag(flags, it->h, it->k, it->l, 1);
		}

Thomas White's avatar
Thomas White committed
448
		delete_items(items);
449
450
	}

451
452
453
454
455
456
457
	image.det = get_detector_geometry(geometry);
	if ( image.det == NULL ) {
		ERROR("Failed to read detector geometry from '%s'\n", geometry);
		return 1;
	}
	free(geometry);

458
459
460
461
462
463
464
	image.beam = get_beam_parameters(beamfile);
	if ( image.beam == NULL ) {
		ERROR("Failed to read beam parameters from '%s'\n", beamfile);
		return 1;
	}
	free(beamfile);

Thomas White's avatar
Thomas White committed
465
	/* Define image parameters */
466
467
	image.width = image.det->max_x + 1;
	image.height = image.det->max_y + 1;
468
	image.lambda = ph_en_to_lambda(eV_to_J(image.beam->photon_energy));
Thomas White's avatar
Thomas White committed
469
470
471
472

	/* Load unit cell */
	input_cell = load_cell_from_pdb(filename);
	if ( input_cell == NULL ) {
Thomas White's avatar
Thomas White committed
473
474
		exit(1);
	}
Thomas White's avatar
Thomas White committed
475
476

	/* Initialise stuff */
477
	image.filename = NULL;
Thomas White's avatar
Thomas White committed
478
479
	image.features = NULL;
	image.flags = NULL;
480
481
	image.f0 = 1.0;
	image.f0_available = 1;
Thomas White's avatar
Thomas White committed
482

483
484
	powder = calloc(image.width*image.height, sizeof(*powder));

Thomas White's avatar
Thomas White committed
485
	/* Splurge a few useful numbers */
486
	STATUS("Wavelength is %f nm\n", image.lambda/1.0e-9);
Thomas White's avatar
Thomas White committed
487

488
489
	do {

Thomas White's avatar
Thomas White committed
490
		int na, nb, nc;
491
		double a, b, c, d;
Thomas White's avatar
Thomas White committed
492
		UnitCell *cell;
Thomas White's avatar
Thomas White committed
493

494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
		if ( random_size ) {

			double alen, blen, clen, dis;

			cell_get_parameters(input_cell, &alen, &blen, &clen,
			                    &dis, &dis, &dis);

			na = random_ncells(alen, min_size, max_size);
			nb = random_ncells(blen, min_size, max_size);
			nc = random_ncells(clen, min_size, max_size);

		} else {

			na = 8;
			nb = 8;
			nc = 8;

		}
Thomas White's avatar
Thomas White committed
512

513
514
		/* Read quaternion from stdin */
		if ( config_randomquat ) {
Thomas White's avatar
Thomas White committed
515
			orientation = random_quaternion();
516
		} else {
Thomas White's avatar
Thomas White committed
517
			orientation = read_quaternion();
518
519
		}

Thomas White's avatar
Thomas White committed
520
		STATUS("Orientation is %5.3f %5.3f %5.3f %5.3f\n",
Thomas White's avatar
Thomas White committed
521
522
		       orientation.w, orientation.x,
		       orientation.y, orientation.z);
Thomas White's avatar
Thomas White committed
523

Thomas White's avatar
Thomas White committed
524
		if ( !quaternion_valid(orientation) ) {
525
			ERROR("Orientation modulus is not zero!\n");
526
527
528
			return 1;
		}

Thomas White's avatar
Thomas White committed
529
530
		cell = cell_rotate(input_cell, orientation);

531
532
533
		/* Ensure no residual information */
		image.data = NULL;
		image.twotheta = NULL;
534

535
		cell_get_parameters(cell, &a, &b, &c, &d, &d, &d);
536
537
538
		STATUS("Particle size = %i x %i x %i (=%5.2f x %5.2f x %5.2f nm)\n",
	               na, nb, nc, na*a/1.0e-9, nb*b/1.0e-9, nc*c/1.0e-9);

539
		if ( config_gpu ) {
540
541
			if ( gctx == NULL ) {
				gctx = setup_gpu(config_nosfac, &image,
542
543
				                 intensities, flags, sym,
				                 gpu_dev);
544
			}
545
			get_diffraction_gpu(gctx, &image, na, nb, nc, cell);
546
		} else {
Thomas White's avatar
Thomas White committed
547
			get_diffraction(&image, na, nb, nc, intensities, phases,
548
549
			                flags, cell, !config_nowater, grad,
			                sym);
550
		}
551
		if ( image.data == NULL ) {
552
553
554
555
			ERROR("Diffraction calculation failed.\n");
			goto skip;
		}

556
		record_image(&image, !config_nonoise);
Thomas White's avatar
Thomas White committed
557

Thomas White's avatar
Thomas White committed
558
		if ( config_nearbragg ) {
559
			find_projected_peaks(&image, cell, 0, 0.1);
560
561
			output_intensities(&image, cell, NULL, 0, 1, 0, stdout,
			                   0, 0.1);
Thomas White's avatar
Thomas White committed
562
			free(image.cpeaks);
Thomas White's avatar
Thomas White committed
563
564
		}

565
		if ( powder_fn != NULL ) {
566
567
568
569
570
571
572

			int x, y, w;

			w = image.width;

			for ( x=0; x<image.width; x++ ) {
			for ( y=0; y<image.height; y++ ) {
573
				powder[x+w*y] += (double)image.data[x+w*y];
574
575
576
577
			}
			}

			if ( !(ndone % 10) ) {
578
				hdf5_write(powder_fn, powder,
579
				           image.width, image.height,
580
				           H5T_NATIVE_DOUBLE);
581
582
583
			}
		}

584
585
586
587
		if ( !config_noimages ) {

			char filename[1024];

588
589
590
591
592
593
594
			if ( n_images != 1 ) {
				snprintf(filename, 1023, "%s-%i.h5",
				         outfile, number);
			} else {
				strncpy(filename, outfile, 1023);
			}

595
			number++;
Thomas White's avatar
Thomas White committed
596

597
598
			/* Write the output file */
			hdf5_write(filename, image.data,
599
			           image.width, image.height, H5T_NATIVE_FLOAT);
600
601

		}
602

603
604
605
		/* Clean up */
		free(image.data);
		free(image.twotheta);
Thomas White's avatar
Thomas White committed
606
		cell_free(cell);
Thomas White's avatar
Thomas White committed
607

608
skip:
Thomas White's avatar
Thomas White committed
609
		ndone++;
610

611
		if ( n_images && (ndone >= n_images) ) done = 1;
Thomas White's avatar
Thomas White committed
612

613
	} while ( !done );
614

615
616
617
618
	if ( gctx != NULL ) {
		cleanup_gpu(gctx);
	}

Thomas White's avatar
Thomas White committed
619
620
	free(image.det->panels);
	free(image.det);
621
	free(image.beam);
622
	free(powder);
Thomas White's avatar
Thomas White committed
623
	cell_free(input_cell);
624
	free(intensities);
Thomas White's avatar
Thomas White committed
625
626
	free(outfile);
	free(filename);
627
	free(sym);
628

629
	return 0;
630
}