pattern_sim.c 16.1 KB
Newer Older
1
/*
Thomas White's avatar
Thomas White committed
2
3
4
 * pattern_sim.c
 *
 * Simulate diffraction patterns from small crystals
5
 *
6
7
8
9
10
 * Copyright © 2012 Deutsches Elektronen-Synchrotron DESY,
 *                  a research centre of the Helmholtz Association.
 *
 * Authors:
 *   2009-2012 Thomas White <taw@physics.org>
11
 *
Thomas White's avatar
Thomas White committed
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 * 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/>.
26
27
28
29
30
31
32
33
34
35
36
37
38
 *
 */


#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
39
#include <getopt.h>
40

41
#include "image.h"
Thomas White's avatar
Thomas White committed
42
#include "diffraction.h"
43
#include "diffraction-gpu.h"
44
#include "cell.h"
45
46
#include "utils.h"
#include "hdf5-file.h"
Thomas White's avatar
Thomas White committed
47
#include "detector.h"
48
#include "peaks.h"
49
#include "beam-parameters.h"
Thomas White's avatar
Thomas White committed
50
#include "symmetry.h"
Thomas White's avatar
Thomas White committed
51
52
#include "reflist.h"
#include "reflist-utils.h"
53
#include "pattern_sim.h"
54
55


Thomas White's avatar
Thomas White committed
56
static void show_help(const char *s)
57
{
58
	printf("Syntax: %s [options]\n\n", s);
Thomas White's avatar
Thomas White committed
59
	printf(
Thomas White's avatar
Thomas White committed
60
"Simulate diffraction patterns from small crystals probed with femtosecond\n"
Thomas White's avatar
Thomas White committed
61
62
"pulses of X-rays from a free electron laser.\n"
"\n"
63
" -h, --help                Display this help message.\n"
Thomas White's avatar
Thomas White committed
64
"\n"
65
66
67
" -p, --pdb=<file>          PDB file from which to get the unit cell.\n"
"                            (The actual Bragg intensities come from the\n"
"                            intensities file)\n"
68
"     --gpu                 Use the GPU to speed up the calculation.\n"
Thomas White's avatar
Thomas White committed
69
70
"     --gpu-dev=<n>         Use GPU device <n>.  Omit this option to see the\n"
"                            available devices.\n"
71
72
" -g, --geometry=<file>     Get detector geometry from file.\n"
" -b, --beam=<file>         Get beam parameters from file.\n"
73
74
75
"\n"
" -n, --number=<N>          Generate N images.  Default 1.\n"
"     --no-images           Do not output any HDF5 files.\n"
76
77
78
79
80
81
" -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
82
" -r, --random-orientation  Use a randomly generated orientation\n"
83
"                            (a new orientation will be used for each image).\n"
84
85
"     --powder=<file>       Write a summed pattern of all images simulated by\n"
"                            this invocation as the given filename.\n"
86
" -i, --intensities=<file>  Specify file containing reflection intensities\n"
Thomas White's avatar
Thomas White committed
87
"                            (and phases) to use.\n"
88
" -y, --symmetry=<sym>      The symmetry of the intensities file.\n"
Thomas White's avatar
Thomas White committed
89
" -t, --gradients=<method>  Use <method> for the calculation of shape\n"
90
91
"                            transform intensities.  Choose from:\n"
"                             mosaic      : Take the intensity of the nearest\n"
Thomas White's avatar
Thomas White committed
92
93
94
"                                           Bragg position.  This is the\n"
"                                           fastest method and the only one\n"
"                                           supported on the GPU.\n"
95
96
"                             interpolate : Interpolate trilinearly between\n"
"                                           six adjacent Bragg intensities.\n"
Thomas White's avatar
Thomas White committed
97
98
"                                           This method has intermediate\n"
"                                           accuracy.\n"
99
"                             phased      : As 'interpolate', but take phase\n"
Thomas White's avatar
Thomas White committed
100
101
102
"                                           values into account.  This is the\n"
"                                           most accurate method, but the\n"
"                                           slowest.\n"
103
104
105
"     --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"
106
107
108
109
110
"     --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
111
"     --no-noise            Do not calculate Poisson noise.\n"
112
);
Thomas White's avatar
Thomas White committed
113
114
115
}


116
117
118
119
static double *intensities_from_list(RefList *list)
{
	Reflection *refl;
	RefListIterator *iter;
120
	double *out = new_arr_intensity();
121
122
123
124
125
126
127
128
129
130

	for ( refl = first_refl(list, &iter);
	      refl != NULL;
	      refl = next_refl(refl, iter) ) {

		signed int h, k, l;
		double intensity = get_intensity(refl);

		get_indices(refl, &h, &k, &l);

131
		set_arr_intensity(out, h, k, l, intensity);
132
133
134
135
136
137
138
139
140
141
142

	}

	return out;
}


static double *phases_from_list(RefList *list)
{
	Reflection *refl;
	RefListIterator *iter;
143
	double *out = new_arr_phase();
144
145
146
147
148
149
150
151
152
153

	for ( refl = first_refl(list, &iter);
	      refl != NULL;
	      refl = next_refl(refl, iter) ) {

		signed int h, k, l;
		double phase = get_phase(refl, NULL);

		get_indices(refl, &h, &k, &l);

154
		set_arr_phase(out, h, k, l, phase);
155
156
157
158
159
160
161
162
163
164
165
166

	}

	return out;

}


static unsigned char *flags_from_list(RefList *list)
{
	Reflection *refl;
	RefListIterator *iter;
167
	unsigned char *out = new_arr_flag();
168
169
170
171
172
173
174
175
176

	for ( refl = first_refl(list, &iter);
	      refl != NULL;
	      refl = next_refl(refl, iter) ) {

		signed int h, k, l;

		get_indices(refl, &h, &k, &l);

177
		set_arr_flag(out, h, k, l, 1);
178
179
180
181
182
183
184
185

	}

	return out;

}


186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
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 {
213
			ERROR("Invalid rotation '%s'\n", line);
214
215
216
		}

	} while ( 1 );
217
218
219
}


220
221
222
223
224
225
226
227
228
229
230
231
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;
}


232
233
int main(int argc, char *argv[])
{
234
	int c;
235
	struct image image;
236
	struct gpu_context *gctx = NULL;
237
	double *powder;
238
239
	char *intfile = NULL;
	double *intensities;
240
	char *rval;
241
	double *phases;
242
	unsigned char *flags;
243
	int config_randomquat = 0;
244
	int config_noimages = 0;
Thomas White's avatar
Thomas White committed
245
	int config_nonoise = 0;
246
	int config_nosfac = 0;
247
	int config_gpu = 0;
248
	int config_random = 0;
249
	char *powder_fn = NULL;
250
	char *filename = NULL;
251
	char *grad_str = NULL;
252
	char *outfile = NULL;
Thomas White's avatar
Thomas White committed
253
	char *geometry = NULL;
254
	char *beamfile = NULL;
255
	GradientMethod grad;
Thomas White's avatar
Thomas White committed
256
257
258
	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 */
259
	int done = 0;
Thomas White's avatar
Thomas White committed
260
261
	UnitCell *input_cell;
	struct quaternion orientation;
Thomas White's avatar
Thomas White committed
262
	int gpu_dev = -1;
263
264
265
	int random_size = 0;
	double min_size = 0.0;
	double max_size = 0.0;
Thomas White's avatar
Thomas White committed
266
267
	char *sym_str = NULL;
	SymOpList *sym;
Thomas White's avatar
Thomas White committed
268

269
	/* Long options */
Thomas White's avatar
Thomas White committed
270
	const struct option longopts[] = {
271
		{"help",               0, NULL,               'h'},
272
		{"gpu",                0, &config_gpu,         1},
273
274
		{"random-orientation", 0, NULL,               'r'},
		{"number",             1, NULL,               'n'},
Thomas White's avatar
Thomas White committed
275
		{"no-images",          0, &config_noimages,    1},
Thomas White's avatar
Thomas White committed
276
		{"no-noise",           0, &config_nonoise,     1},
277
		{"intensities",        1, NULL,               'i'},
278
		{"symmetry",           1, NULL,               'y'},
279
		{"powder",             1, NULL,               'w'},
Thomas White's avatar
Thomas White committed
280
		{"gradients",          1, NULL,               't'},
281
		{"pdb",                1, NULL,               'p'},
282
		{"output",             1, NULL,               'o'},
Thomas White's avatar
Thomas White committed
283
		{"geometry",           1, NULL,               'g'},
284
		{"beam",               1, NULL,               'b'},
285
		{"really-random",      0, &config_random,      1},
Thomas White's avatar
Thomas White committed
286
		{"gpu-dev",            1, NULL,                2},
287
288
		{"min-size",           1, NULL,                3},
		{"max-size",           1, NULL,                4},
289
		{0, 0, NULL, 0}
Thomas White's avatar
Thomas White committed
290
	};
291

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

Thomas White's avatar
Thomas White committed
296
		switch (c) {
Thomas White's avatar
Thomas White committed
297
298

			case 'h' :
Thomas White's avatar
Thomas White committed
299
300
			show_help(argv[0]);
			return 0;
301

Thomas White's avatar
Thomas White committed
302
			case 'r' :
303
304
305
			config_randomquat = 1;
			break;

Thomas White's avatar
Thomas White committed
306
			case 'n' :
307
308
309
310
311
			n_images = strtol(optarg, &rval, 10);
			if ( *rval != '\0' ) {
				ERROR("Invalid number of images.\n");
				return 1;
			}
312
313
			break;

Thomas White's avatar
Thomas White committed
314
			case 'i' :
315
316
317
			intfile = strdup(optarg);
			break;

Thomas White's avatar
Thomas White committed
318
			case 't' :
319
320
321
			grad_str = strdup(optarg);
			break;

Thomas White's avatar
Thomas White committed
322
			case 'p' :
323
324
325
			filename = strdup(optarg);
			break;

Thomas White's avatar
Thomas White committed
326
			case 'o' :
327
328
329
			outfile = strdup(optarg);
			break;

Thomas White's avatar
Thomas White committed
330
			case 'w' :
331
332
333
			powder_fn = strdup(optarg);
			break;

Thomas White's avatar
Thomas White committed
334
			case 'g' :
Thomas White's avatar
Thomas White committed
335
336
337
			geometry = strdup(optarg);
			break;

Thomas White's avatar
Thomas White committed
338
			case 'b' :
339
340
341
			beamfile = strdup(optarg);
			break;

Thomas White's avatar
Thomas White committed
342
			case 'y' :
Thomas White's avatar
Thomas White committed
343
			sym_str = strdup(optarg);
344
345
			break;

Thomas White's avatar
Thomas White committed
346
			case 2 :
Thomas White's avatar
Thomas White committed
347
348
349
			gpu_dev = atoi(optarg);
			break;

Thomas White's avatar
Thomas White committed
350
			case 3 :
351
352
353
354
355
			min_size = strtod(optarg, &rval);
			if ( *rval != '\0' ) {
				ERROR("Invalid minimum size.\n");
				return 1;
			}
356
357
358
			random_size++;
			break;

Thomas White's avatar
Thomas White committed
359
			case 4 :
360
361
362
363
364
			max_size = strtod(optarg, &rval);
			if ( *rval != '\0' ) {
				ERROR("Invalid maximum size.\n");
				return 1;
			}
365
366
367
			random_size++;
			break;

Thomas White's avatar
Thomas White committed
368
			case 0 :
Thomas White's avatar
Thomas White committed
369
			break;
370

Thomas White's avatar
Thomas White committed
371
			default :
372
373
			ERROR("Unhandled option '%c'\n", c);
			break;
Thomas White's avatar
Thomas White committed
374

Thomas White's avatar
Thomas White committed
375
		}
376
377
378

	}

379
	if ( config_random ) {
380
381
382
383
384
385
386
387
		FILE *fh;
		unsigned int seed;
		fh = fopen("/dev/urandom", "r");
		fread(&seed, sizeof(seed), 1, fh);
		fclose(fh);
		srand(seed);
	}

388
389
390
391
392
	if ( random_size == 1 ) {
		ERROR("You must specify both --min-size and --max-size.\n");
		return 1;
	}

393
394
395
396
	if ( filename == NULL ) {
		filename = strdup("molecule.pdb");
	}

397
398
399
400
401
402
403
404
	if ( outfile == NULL ) {
		if ( n_images == 1 ) {
			outfile = strdup("sim.h5");
		} else {
			outfile = strdup("sim");
		}
	}

Thomas White's avatar
Thomas White committed
405
406
407
	if ( sym_str == NULL ) sym_str = strdup("1");
	sym = get_pointgroup(sym_str);
	/* sym_str is used below */
408

409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
	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
431
432
433
434
435
	if ( geometry == NULL ) {
		ERROR("You need to specify a geometry file with --geometry\n");
		return 1;
	}

436
437
438
439
440
441
	if ( beamfile == NULL ) {
		ERROR("You need to specify a beam parameter file"
		      " with --beam\n");
		return 1;
	}

442
	if ( intfile == NULL ) {
443

444
445
446
447
448
449
		/* 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;
450
451
		flags = NULL;

452
	} else {
453

Thomas White's avatar
Thomas White committed
454
455
456
		RefList *reflections;

		reflections = read_reflections(intfile);
Thomas White's avatar
Thomas White committed
457
458
459
460
		if ( reflections == NULL ) {
			ERROR("Problem reading input file %s\n", intfile);
			return 1;
		}
Thomas White's avatar
Thomas White committed
461
		free(intfile);
462

463
		if ( grad == GRADIENT_PHASED ) {
Thomas White's avatar
Thomas White committed
464
			phases = phases_from_list(reflections);
465
466
467
		} else {
			phases = NULL;
		}
Thomas White's avatar
Thomas White committed
468
469
470
		intensities = intensities_from_list(reflections);
		phases = phases_from_list(reflections);
		flags = flags_from_list(reflections);
471

Thomas White's avatar
Thomas White committed
472
		/* Check that the intensities have the correct symmetry */
Thomas White's avatar
Thomas White committed
473
		if ( check_list_symmetry(reflections, sym) ) {
Thomas White's avatar
Thomas White committed
474
			ERROR("The input reflection list does not appear to"
Thomas White's avatar
Thomas White committed
475
			      " have symmetry %s\n", symmetry_name(sym));
Thomas White's avatar
Thomas White committed
476
477
478
			return 1;
		}

Thomas White's avatar
Thomas White committed
479
480
		reflist_free(reflections);

481
482
	}

483
484
485
486
487
488
489
	image.det = get_detector_geometry(geometry);
	if ( image.det == NULL ) {
		ERROR("Failed to read detector geometry from '%s'\n", geometry);
		return 1;
	}
	free(geometry);

490
491
492
493
494
495
496
	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
497
	/* Define image parameters */
Thomas White's avatar
Thomas White committed
498
499
	image.width = image.det->max_fs + 1;
	image.height = image.det->max_ss + 1;
500
	image.lambda = ph_en_to_lambda(eV_to_J(image.beam->photon_energy));
501
502
	image.bw = image.beam->bandwidth;
	image.div = image.beam->divergence;
Thomas White's avatar
Thomas White committed
503
504
505
506

	/* Load unit cell */
	input_cell = load_cell_from_pdb(filename);
	if ( input_cell == NULL ) {
Thomas White's avatar
Thomas White committed
507
508
		exit(1);
	}
Thomas White's avatar
Thomas White committed
509
510

	/* Initialise stuff */
511
	image.filename = NULL;
Thomas White's avatar
Thomas White committed
512
513
	image.features = NULL;
	image.flags = NULL;
Thomas White's avatar
Thomas White committed
514

515
516
	powder = calloc(image.width*image.height, sizeof(*powder));

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

520
521
	do {

Thomas White's avatar
Thomas White committed
522
		int na, nb, nc;
523
		double a, b, c, d;
Thomas White's avatar
Thomas White committed
524
		UnitCell *cell;
Thomas White's avatar
Thomas White committed
525

526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
		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
544

545
546
		/* Read quaternion from stdin */
		if ( config_randomquat ) {
Thomas White's avatar
Thomas White committed
547
			orientation = random_quaternion();
548
		} else {
Thomas White's avatar
Thomas White committed
549
			orientation = read_quaternion();
550
551
		}

Thomas White's avatar
Thomas White committed
552
		STATUS("Orientation is %5.3f %5.3f %5.3f %5.3f\n",
Thomas White's avatar
Thomas White committed
553
554
		       orientation.w, orientation.x,
		       orientation.y, orientation.z);
Thomas White's avatar
Thomas White committed
555

Thomas White's avatar
Thomas White committed
556
		if ( !quaternion_valid(orientation) ) {
557
			ERROR("Orientation modulus is not zero!\n");
558
559
560
			return 1;
		}

Thomas White's avatar
Thomas White committed
561
562
		cell = cell_rotate(input_cell, orientation);

563
564
565
		/* Ensure no residual information */
		image.data = NULL;
		image.twotheta = NULL;
566

567
		cell_get_parameters(cell, &a, &b, &c, &d, &d, &d);
Thomas White's avatar
Thomas White committed
568
569
		STATUS("Particle size = %i x %i x %i"
		       " ( = %5.2f x %5.2f x %5.2f nm)\n",
570
571
	               na, nb, nc, na*a/1.0e-9, nb*b/1.0e-9, nc*c/1.0e-9);

572
		if ( config_gpu ) {
573
			if ( gctx == NULL ) {
574
				gctx = setup_gpu(config_nosfac,
Thomas White's avatar
Thomas White committed
575
				                 intensities, flags, sym_str,
576
				                 gpu_dev);
577
			}
578
			get_diffraction_gpu(gctx, &image, na, nb, nc, cell);
579
		} else {
Thomas White's avatar
Thomas White committed
580
			get_diffraction(&image, na, nb, nc, intensities, phases,
Thomas White's avatar
Thomas White committed
581
			                flags, cell, grad, sym);
582
		}
583
		if ( image.data == NULL ) {
584
585
586
587
			ERROR("Diffraction calculation failed.\n");
			goto skip;
		}

588
		record_image(&image, !config_nonoise);
Thomas White's avatar
Thomas White committed
589

590
		if ( powder_fn != NULL ) {
591
592
593
594
595
596
597

			int x, y, w;

			w = image.width;

			for ( x=0; x<image.width; x++ ) {
			for ( y=0; y<image.height; y++ ) {
598
				powder[x+w*y] += (double)image.data[x+w*y];
599
600
601
602
			}
			}

			if ( !(ndone % 10) ) {
603
				hdf5_write(powder_fn, powder,
604
				           image.width, image.height,
605
				           H5T_NATIVE_DOUBLE);
606
607
608
			}
		}

609
610
611
612
		if ( !config_noimages ) {

			char filename[1024];

613
614
615
616
617
618
619
			if ( n_images != 1 ) {
				snprintf(filename, 1023, "%s-%i.h5",
				         outfile, number);
			} else {
				strncpy(filename, outfile, 1023);
			}

620
			number++;
Thomas White's avatar
Thomas White committed
621

622
			/* Write the output file */
623
			hdf5_write_image(filename, &image);
624
625

		}
626

627
628
629
		/* Clean up */
		free(image.data);
		free(image.twotheta);
Thomas White's avatar
Thomas White committed
630
		cell_free(cell);
Thomas White's avatar
Thomas White committed
631

632
skip:
Thomas White's avatar
Thomas White committed
633
		ndone++;
634

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

637
	} while ( !done );
638

639
640
641
642
643
644
	if ( powder_fn != NULL ) {
		hdf5_write(powder_fn, powder,
		           image.width, image.height,
		           H5T_NATIVE_DOUBLE);
	}

645
646
647
648
	if ( gctx != NULL ) {
		cleanup_gpu(gctx);
	}

Thomas White's avatar
Thomas White committed
649
650
	free(image.det->panels);
	free(image.det);
651
	free(image.beam);
652
	free(powder);
Thomas White's avatar
Thomas White committed
653
	cell_free(input_cell);
654
	free(intensities);
Thomas White's avatar
Thomas White committed
655
656
	free(outfile);
	free(filename);
Thomas White's avatar
Thomas White committed
657
658
	free(sym_str);
	free_symoplist(sym);
659

660
	return 0;
661
}